我使用原始/裸机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
我该如何测试?什么是测试这样的原始sql插件的好策略?
答案 0 :(得分:5)
关于如何正确测试代码,有两种思想流派:'方法'透视是关于确保你自己的正确行为,以及结果'透视有利于检查结果。我将提供两种观点。
<强>方法:强>
您没有写ActiveRecord::Base.connection.execute
,并且您不对其正常工作负责。那么,您的测试可以专注于您传递给execute方法的内容。那么匹配的规范可能看起来像这样。
connection = double("Connection")
expect(ActiveRecord::Base).to receive(:connection) { connection }
expect(connection).to receive(:execute).with("...")
Worker.new.perform
其中...是您要生成的SQL。根据您的喜好,验证SQL是否正常工作可能会在其他地方完成,或者根本不会完成。这可以向您(以及将来的开发人员)保证已将正确的指令传送到您的数据库。
<强>结果:强>
这种方法并没有找到保证采取何种方法,它在结果中找到了保证。这是你在上面的例子中采用的方法。
您没有向我们展示perform
方法上面的内容,因此我们不确定您的实施代码是否存在问题,而不是{{ 1}}您用来提示ActiveRecord注意您的更改的机制。 ActiveRecord具有各种性能增强功能,旨在通过云直接绑定数据库内容与您在Ruby VM对象图中可以访问的内容。如果您愿意,可以尝试覆盖此行为,但如果您希望直接在实现中使用ActiveRecord,那么您在测试中也可能满足于这样做?
reload
但是,我注意到这里有一些令人不安的推论。您正在编辑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")
表,但期望得到的结果可能是history
模型。然后,Device
方法的实现也与您的问题相关。如果选择此方法,您可能希望为status
方法编写其他测试。