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

kha*_*hal 9 ruby activerecord rspec ruby-on-rails

我正在使用原始/裸机sql插件来提高我的服务的写入性能.我的模块中有这样的东西 -

insert = "('#{id}', '#{status}', '#{some_time_val}')"
sql_string = "INSERT INTO history ('device_id', 'status', 'time') VALUES #{insert}"
ActiveRecord::Base.connection.execute sql_string
Run Code Online (Sandbox Code Playgroud)

当我写下面的rspec时,除了插入是否通过之外,它还会测试所有内容.所以我的期望永远不会有效,因为rspec,database_cleaner等做回滚和交易的方式.我试过用

  self.use_transactional_fixtures = false
Run Code Online (Sandbox Code Playgroud)

  before(:all) do
    DatabaseCleaner.strategy = nil
  end
Run Code Online (Sandbox Code Playgroud)

但插入仍然没有进入我的测试数据库

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
Run Code Online (Sandbox Code Playgroud)

我该如何测试?测试这样的原始sql插件有什么好策略?

Jim*_*eet 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
Run Code Online (Sandbox Code Playgroud)

其中...是您打算生成的SQL。根据您的喜好,验证SQL是否正常工作可能在其他地方完成,或者根本没有完成。这可以确保您(以及将来的开发人员)已将正确的指令传递到数据库。

结果:

这种方法并不能保证采用哪种方法,而是可以保证结果。这是您在以上示例中采用的方法。

您没有向我们展示perform上面方法的外观,所以我们不确定您的实现代码是否有问题,而不是reload您用来提示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")
Run Code Online (Sandbox Code Playgroud)

我在这里注意到一些令人不安的推论。您正在编辑history表格,但是期望从大概的Device模型中得到结果。然后,该status方法的实现也与您的问题有关。Device#status如果选择此方法,则可能希望为该方法编写其他测试。