我正在使用原始/裸机sql插件来提高我的服务的写入性能.我的模块里有这样的东西 –
insert = "('#{id}','#{status}','#{some_time_val}')" sql_string = "INSERT INTO history ('device_id','status','time') VALUES #{insert}" ActiveRecord::Base.connection.execute sql_string
当我写下面的rspec时,除了插入是否通过外,它还会测试所有内容.所以我的期望永远不会有效,因为rspec,database_cleaner等做回滚和交易的方式.
我试过用
self.use_transactional_fixtures = false
和
before(:all) do DatabaseCleaner.strategy = nil end
但插入仍然没有进入我的测试数据库
describe Worker do let (:device1) {FactoryGirl.create(:device)} let (:device2) {FactoryGirl.create(:device)} let (:device3) {FactoryGirl.create(:device)} self.use_transactional_fixtures = false before(:all) do DatabaseCleaner.strategy = nil end it "does something" do devices = [{"status" => "Offline","time" => "2013-09-17 18:17:17","id" => device1.id},{"status" => "Online","time" => "2013-09-17 18:18:18","id" => device2.id}] Worker.any_instance.stubs(:devices).returns(devices) ## Not important for this question Worker.new.perform device1.reload.status.should == "Offline" # FAILS end end
解决方法
关于如何正确测试代码有两种思路:“方法”视角是关于确保自己的正确行为,“结果”视角有利于检查结果.我将提供两种观点.
方法:
您没有编写ActiveRecord :: Base.connection.execute,并且您不对其正常工作负责.那么,您的测试可以专注于您传递给execute方法的内容.那么,匹配的规范可能看起来像这样.
connection = double("Connection") expect(ActiveRecord::Base).to receive(:connection) { connection } expect(connection).to receive(:execute).with("...") Worker.new.perform
where …是您要生成的sql.根据您的喜好,验证sql是否正常工作可能会在其他地方完成,或者根本不会完成.这可以向您(以及将来的开发人员)保证已将正确的指令传送到您的数据库.
结果:
这种方法没有找到保证采取何种方法,它在结果中找到了保证.这是您在上述示例中采用的方法.
您没有告诉我们上面的执行方法是什么样的,所以我们不确定您的实现代码是否存在问题,而不是您用来提示ActiveRecord注意到您的更改的重新加载机制. ActiveRecord具有各种性能增强功能,旨在通过云直接绑定数据库内容与您在Ruby VM对象图中可以访问的内容.如果您愿意,可以尝试覆盖此行为,但如果您满足于在实现中直接使用ActiveRecord,那么您在测试中也可能满足于这样做?
d1_status = ActiveRecord::Base.connection.execute("SELECT status from history where id = #{device1.id} limit 1;").values[0][0] expect(d1_status).to eq("Offline")
不过,我在这里注意到一些令人不安的推论.您正在编辑历史记录表,但期望得到的结果可能是设备模型.然后,状态方法的实现也与您的问题相关.如果选择此方法,您可能希望为Device#status方法编写其他测试.