1

I am using rpsec to assert that a certain method generates the expected query:

describe '.customers' do

    let(:segment) { create(:segment) }
    
    let!(:rule1) do
      Rule.create!(segment: segment, filter_key: 'generic',
                   attr_name: 'first_name', operator: '=', values: ['john'])
    end
    
    let!(:rule2) do
      Rule.create!(segment: segment, filter_key: 'generic',
                   attr_name: 'orders_count', operator: '>=', values: ['3'])
    end
    
    let(:actual_query) { segment.customers.to_sql }
    let(:expected_query) do
      Customer
        .where('first_name = john')
        .where('orders_count >= 3')
        .to_sql
    end
    
    it 'applies all the rules scopes to customer' do
      expect(actual_query).to eq(expected_query)
    end

end

It usually passes, the thing is that sometimes (for some reason) the where clauses in one of the sql queries get reordered so, since the to_sql is actually the string of the query to execute, sometimes it fails saying:

Failure/Error: expect(actual_query).to eq(expected_query)

 expected: "SELECT \"customers\".* FROM \"customers\" WHERE (orders_count >= '3') AND (first_name = 'john')"

    got: "SELECT \"customers\".* FROM \"customers\" WHERE (first_name = 'john') AND (orders_count >= '3')"
 (compared using ==)

when they are actually the same in terms of the execution. Any ideas on how else to assert the two queries are the same without having to add test customers to compare the queries after they executed?

1 Answer 1

1

It looks that it is related to ordering of your rules in segment.customers. As they are created one just after another, it's possible that their timestamps in created_at are the same.

I would order them explicitly and add data that supports it in the spec, e.g.:

let!(:rule1) do
  Rule.create!(segment: segment, filter_key: 'generic',
               attr_name: 'first_name', operator: '=', values: ['john'],
               created_at: 2.days.ago)
end

let!(:rule2) do
  Rule.create!(segment: segment, filter_key: 'generic',
               attr_name: 'orders_count', operator: '>=', values: ['3'],
               created_at: 1.day.ago)
end
Sign up to request clarification or add additional context in comments.

3 Comments

What if when I create the query I just concat the rules using rules.order(:id).each do .... would that be enough? (asking because it's hard to test since error appears rarely)
That should be enough, if your database assign ids monotonically, i.e. next id is bigger than the previous one.
Awesome. I will make sure to run it a couple times before marking as answer thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.