ruby 存根ActiveRecord::与ActiveRecord对象的关系

nzkunb0c  于 2022-11-22  发布在  Ruby
关注(0)|答案(2)|浏览(151)

我不是在测试Rails应用程序。只是把它弄清楚。
我正在测试一个库,它连接到一个相对活跃的服务器,通过时间戳限制记录。这些返回的记录随着时间的推移而变化,使得测试其他限制更加复杂。我需要stub出ActiveRecord::where方法来返回我自己的自定义关系,这些关系是我创建的,以满足我所需要的条件。
比如

relation = double(ActiveRecord::Relation)
relation.stub(:[]).and_return( [MyClass.new(...), MyClass.new(...), ...] )
MyClass.stub(:where).and_return( relation )

是我想要的,但是那不起作用。我需要它是一个ActiveRecord::Relation,因为我需要能够在代码中对对象调用ActiveRecord::whereActiveRecord::select
2014年1月28日**编辑
在lib中/调用.rb

class Call < ActiveRecord::Base
  class << self
    def sales start_time, end_time
      restricted_records = records(start_time, end_time, :agent_id)
      #other code
    end

    #other methods

    private

      def records start_time, end_time, *select
        # I'm leaving in commented code so you can see why I want the ActiveRecord::Relation object, not an Array
        calls = Call.where("ts BETWEEN '#{start_time}' AND '#{end_time}'") #.select(select)
        raise calls.inspect
          #.to_a.map(&:serializable_hash).map {|record| symbolize(record)}
      end
  end
end

在规格/call_spec.rb中

require 'spec_helper'
require 'call.rb'

describe Call do
  let(:period_start) { Time.now - 60 }
  let(:period_end) { Time.now }

  describe "::sales" do
    before do
      relation = Call.all
      relation.stub(:[]).and_return( [Call.new(queue: "12345")] )
      Call.stub(:where).and_return( relation )
    end

    subject { Call.sales(period_start, period_end) }

    it "restricts results to my custom object" do
      subject
    end
  end
end

测试输出:

RuntimeError:
  #<ActiveRecord::Relation [ #an array containing all the actual Call records, not my object ]>
eqoofvh9

eqoofvh91#

ActiveRecord::Relation 是 一 个 类 , :[] 是 该类 的 一 个 示例 方法 。 您 正在 存根 类 本身 的 一 个 方法 , 因此 它 不会 被 任何 Rails 代码 调用 。
如果 您 希望 MyClass.where 返回 一 个 只 包含 :[] 存根 的 关系 , 则 必须 首先 创建 一 个 Relation 示例 , 如下 所 示 :

relation = MyClass.all
relation.stub(:[]).and_return( [MyClass.new(...), MyClass.new(...), ...] )
MyClass.stub(:where).and_return( relation )

中 的 每 一 个
但是 , 请 注意 , 为了 在 此 上下 文 中 获得 返回 的 数组 , 您 需要 执行 以下 操作 :

MyClass.where("ignored parameters")["ignored parameters"]

格式
此外 , 如果 您 随后 在 relation 上 调用 where , 您 将 返回 Relation 的 一 个 * new * 示例 , 该 示例 将 不再 被 存根 。

vfh0ocws

vfh0ocws2#

2022年更新
前面的答案是完全错误的,因为它不适用于索引、.to_a.first.last.any?.none?以及几乎所有其他方法。
相反,您可以通过存根关系的records方法来模拟关系中包含的记录。

custom_records = ["a", "b", "c"]

relation = Model.all
relation.stub(:records).and_return(custom_records)

allow(Model).to receive(:where).and_return(relation)

# Later ...

records = Model.where('1 + 1 = 2') # content of the query doesn't matter, .where is mocked
records.first # => "a"
records.last # => "c"
records.to_a # => ["a", "b", "c"]
records.any? { |x| x == "b" } # => true

大多数方法都可以工作,但是有一些例外需要单独处理。

  • .count-直接调用一个SELECT COUNT(*) SQL查询,绕过我们的records模拟。修复:
relation.stub(:count).and_return(custom_records.count)
  • .exists?-直接调用另一个SQL查询,再次绕过我们的records模拟。修复:
relation.stub(:exists?).and_return(custom_records.present?)
  • others -可能还有其他方法需要存根(取决于代码是否使用这些方法),您可以根据需要对每个方法进行存根

此外,您还可以模拟has_many关系的返回值(这是我在Google上搜索此问题时的实际用例),方法是:

allow(record).to receive(:related_records).and_wrap_original do |original, *args, &block|
  relation = original.call(*args, &block)
  relation.stub(:records).and_return(my_custom_array_of_related_records)
  relation
end

相关问题