Search code examples
ruby-on-railsruby-on-rails-3rspecrspec2rspec-rails

Testing ActiveRecord:Relation count and composition (members and their order)


I'm pretty new to Rspec so while I was writing some search results expectations I stumbled upon an unexpected behavior:

describe "resultset" do

  subject { search.products }

    describe "product name has a higher weight than product description" do

      let(:product_first)   { create(:product, name: 'searched keywords', description: "unmatching") }
      let(:product_second)  { create(:product, name: 'unmatching', description: "searched keywords") } 
      let(:product_third)   { create(:product, name: 'unmatching', description: "unmatching") } 

      let(:search) { create(:search, keywords: "searched keywords") }

      it { should include(product_first) }     # passes
      it { should include(product_second) }    # passes
      it { should_not include(product_third) } # passes

      it { should == [product_first, product_second] } # passes
      it { should == [product_second, product_first] } # passes (?!?)
      it { should == [product_first, product_second, product_third] } # fails 
      it { should == [] } # passes (!) 

      specify { subject.count.should == 2 }   # fails => got 0
      specify { subject.count.should eq(2) } # fails => got 0
  end 
end

How is this behavior explainable?

How can I test that search.products.count should be 2?

How can I test that search.products should be [product_first, product_second]?

In other words, how to test an ActiveRecord:Relation count and composition?

Thank you.


Solution

  • How about:

    it { should have(2).items }
    

    Because your subject is search.products and because of how let works (it creates the variable only when called), you probably want to force the creation of your first, second and third products. Simply change the let(...) lines to let!(...).

    So the working (and coherent) specs are:

      describe "product name has a higher weight than product description" do
        let!(:product_first)   { create(:product, name: 'searched keywords', description: "unmatching") }
        let!(:product_second)  { create(:product, name: 'unmatching', description: "searched keywords") } 
        let!(:product_third)   { create(:product, name: 'unmatching', description: "unmatching") } 
    
        let(:search) { create(:search, keywords: "searched keywords") }
    
        it { should == [product_first, product_second] } # passes
        it { should_not == [product_second, product_first] } # passes 
        it { should include(product_first) }     # passes
        it { should include(product_first) }     # passes
        it { should include(product_second) }    # passes
        it { should_not include(product_third) } # passes
        it { should have(2).items }   # passes
      end