I'm not testing a Rails app. Just getting that out of the way.
I'm testing a library that connects to a relatively active server, restricting records by timestamp. These returned records change as time goes on, making testing other restrictions more complicated. I need to stub out the ActiveRecord::where
method to return my own custom relation with objects I create to meet the criteria I need.
Something like
relation = double(ActiveRecord::Relation)
relation.stub(:[]).and_return( [MyClass.new(...), MyClass.new(...), ...] )
MyClass.stub(:where).and_return( relation )
is what I'd like, but that doesn't work. I need it to be an ActiveRecord::Relation
because I need to be able to call ActiveRecord::where
and ActiveRecord::select
on the object in the code.
Edit 2014-01-28
In lib/call.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
In spec/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
Output from test:
RuntimeError:
#<ActiveRecord::Relation [ #an array containing all the actual Call records, not my object ]>
An instance of ActiveRecord::Base is an object that represents a specific row of your database (or might be saved into the database). Whereas an instance of ActiveRecord::Relation is a representation of a query that can be run against your database (but wasn't run yet).
ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending. Edit: as Mike points out, in this case ActiveRecord is a module... ActiveRecord is defined as a module in Rails, github.com/rails/rails/tree/master/activerecord/lib/…
The key thing to note is that #find returns the actual record while #where returns an ActiveRecord::Relation which basically acts like an array.
Returns a new relation, which is the result of filtering the current relation according to the conditions in the arguments.
ActiveRecord::Relation
is a class and :[]
is an instance method of that class. You're stubbing a method of the class itself, so it's not going to be invoked by any of the Rails code.
If you want MyClass.where
to return a relation with just the :[]
stubbed, you'll have to create a Relation instance first, as in:
relation = MyClass.all
relation.stub(:[]).and_return( [MyClass.new(...), MyClass.new(...), ...] )
MyClass.stub(:where).and_return( relation )
However, note that in order to get to your returned array in this context, you'll need to do:
MyClass.where("ignored parameters")["ignored parameters"]
Further, if you subsequently call where
on relation
, you'll return a new instance of Relation
which will no longer be stubbed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With