Search code examples
ruby-on-railsactiverecordrspecrspec-rails

ActiveRecord#where returning instance when running specs


I'm writing specs for some software and getting a weird behavior: In the method I'm testing, I'm calling:

MyModel.where(special_identifier: opts[:ident]).first || MyModelBuilder.new(m).build 

(note I don't want first_or_create or first_or_new since I'm using explicit builders here). When I query MyModel.where(...) I should be getting back an ActiveRecord::Relation object, which is how it behaves in prod, dev, console, etc. However, when running the specs, where returns a MyModel object. Is this a bug, am I missing something?


Solution

  • As @DVG said in the comments, MyModel.where(...) returns a relation, but calling .first or .last on it should return an instance, not a relation. Similarly, if the where returns more than one record, then .limit(...), .to_a, or .all on the relation would return the set of records.

    If you are doing MyModel.where(...) and it is definitely not returning a relation, then you need to figure out where that is happening:

    if !MyModel.where('').is_a? ActiveRecord::Relation
      raise "where was defined in #{MyModel.method(:where).source_location}"
    end
    

    That raise could be a fail instead, if in a spec.

    If that doesn't work, see this answer for more ways to get the source of the method.

    Now if .first returns nil, then your code:

    MyModel.where(special_identifier: opts[:ident]).first || MyModelBuilder.new(m).build
    

    is going to return whatever MyModelBuilder.new(m).build returns (or it might raise something).