首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >存根ActiveRecord::与ActiveRecord对象的关系

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

Stack Overflow用户
提问于 2014-01-25 00:35:08
回答 2查看 18.3K关注 0票数 11

我不是在测试Rails应用程序。别挡道了。

我正在测试一个连接到相对活跃的服务器的库,通过时间戳限制记录。这些返回的记录随着时间的推移而改变,使测试其他限制变得更加复杂。我需要对ActiveRecord::where方法进行存根,以返回我创建的对象的自定义关系,以满足我需要的条件。

有点像

代码语言:javascript
复制
relation = double(ActiveRecord::Relation)
relation.stub(:[]).and_return( [MyClass.new(...), MyClass.new(...), ...] )
MyClass.stub(:where).and_return( relation )

是我想要的,但那不管用。我需要它是一个ActiveRecord::Relation,因为我需要能够在代码中的对象上调用ActiveRecord::whereActiveRecord::select

编辑2014-01-28

在lib/call.rb中

代码语言:javascript
复制
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

规范/调用规范. In

代码语言:javascript
复制
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

测试输出:

代码语言:javascript
复制
RuntimeError:
  #<ActiveRecord::Relation [ #an array containing all the actual Call records, not my object ]>
EN

回答 2

Stack Overflow用户

发布于 2014-01-26 16:39:13

ActiveRecord::Relation是类,:[]是该类的实例方法。您正在阻塞类本身的一个方法,所以它不会被任何Rails代码调用。

如果希望MyClass.where返回带有:[]存根的关系,则必须首先创建关系实例,如下所示:

代码语言:javascript
复制
relation = MyClass.all
relation.stub(:[]).and_return( [MyClass.new(...), MyClass.new(...), ...] )
MyClass.stub(:where).and_return( relation )

但是,请注意,为了在此上下文中访问返回的数组,需要执行以下操作:

代码语言:javascript
复制
MyClass.where("ignored parameters")["ignored parameters"]

此外,如果随后在where上调用relation,您将返回一个新的Relation实例,该实例将不再被删除。

票数 5
EN

Stack Overflow用户

发布于 2022-11-17 18:17:46

更新2022

前一个被否决的答案是完全不正确的,因为索引、.to_a.first.last.any?.none?,几乎所有其他方法都不适用。

相反,您可以通过阻塞关系的records方法来模拟包含在关系中的记录。

代码语言:javascript
复制
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模拟。Fix: relation.stub(:count).and_return(custom_records.count)

  • .exists? -直接调用另一个SQL查询,再次绕过我们的records模拟。Fix: relation.stub(:exists?).and_return(custom_records.present?)

  • 其他方法可能需要存根(取决于您的代码是否使用这些方法),您可以根据需要对每个方法进行存根。

此外,您可以通过以下操作来模拟has_many关系的返回值(这是我搜索此问题时的实际用例)

代码语言:javascript
复制
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
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21344992

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档