【问题标题】:Rspec: How to test ActiveRecord::Base.connection.executeRspec:如何测试 ActiveRecord::Base.connection.execute
【发布时间】:2015-04-04 17:46:48
【问题描述】:

我正在使用原始/裸机 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 插入的好策略是什么?

【问题讨论】:

  • 您绝对不希望没有数据库清理。至少你想要截断。不清洗意味着测试污染。另请注意,您的查询存在 SQL 注入漏洞。
  • 我想不出你不能“正常”测试这个的原因(我认为数据库清理器是一个红鲱鱼)。如果您使用正常的活动记录而不是execute,您的测试是否通过(我似乎很奇怪,您正在重新加载现有对象以测试其状态,而您发布的 sn-p 似乎将新行插入到不同的表中)
  • 在沙盒控制台上运行查询时的输出是什么?在我看来,您的查询缺少最后一个“;”。
  • 一般来说,这个工作流程是可测试的。我建议直接在您的数据库中验证您的 SQL。也许您需要使用严重字符而不是撇号(`device_id` 而不是 'device_id')...这取决于您的 SQL 引擎。

标签: ruby-on-rails ruby activerecord rspec


【解决方案1】:

关于如何正确测试代码有两种思想流派:“方法”观点是关于确保您自己的正确行为,“结果”观点有利于检查结果。我将提供两种观点。

方法:

你没有写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 方法是什么样的,所以我们不确定您的实现代码是否存在问题,而不是您使用的 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")

不过,我注意到这里有一些令人不安的推论。您正在编辑history 表,但期待来自Device 模型的结果。 status 方法的实现也与您的问题有关。如果您选择此方法,您可能希望为 Device#status 方法编写额外的测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-12
    • 1970-01-01
    • 2017-07-17
    • 2012-11-20
    • 2011-11-10
    • 1970-01-01
    相关资源
    最近更新 更多