Rspec:如何测试ActiveRecord :: Base.connection.execute

时间:2015-02-05 00:48:10

标签: ruby-on-rails ruby activerecord rspec

我使用原始/裸机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插件的好策略?

1 个答案:

答案 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方法编写其他测试。