Search code examples
ruby-on-railsrubyunit-testingrspecrspec-rails

Spec for class method - preserving order and returns an ActiveRecord relation


Pretty new on Rails and currently learning out how to tackle class methods, scope and spec... Initially this was written as a scope in a model but it seems a bit bloated so I've taken it out and made it into class method like below:

class OrderedByIds

  scope :for_ids_with_order, lambda { |ids|
    order = sanitize_sql_array(
      ["position((',' || id::text || ',') in ?)", ids.join(',') + ',']
    )
    where(id: ids).order(order)
  }

end

# usage:

 OrderedByIds.for_ids_with_order([1, 3, 2])

What's the best way to unit test for this class method?


Solution

  • You can't really Unit test this, because it's tightly coupled with the database. So Rails model test are not really unit tests.

    So with that out of the way, you can test it and it's pretty simple. (I'll assume you're using FactoryBot and your rspec is Aggregating failures )

    let!(:order) { create(:order) }
    let!(:other_order) { create(:order) }
    
    specify do
       expect(describe_class.for_ids_with_order([order.id])).to eq [order]
       expect(describe_class.for_ids_with_order([other_order.id])).to eq [other_order]
    end
    

    Then, you can do another context that will test the ordering: I'd create 3 or more items, with specified positions and make an expectation like above but you're expecting an array of few items with a particular order (I'm not 100% sure what "position((',' || id::text || ',') in ?)" is doing there, hence no example. But this should get you going).

    BTW. if it was a scope, I'd test it exactly the same way.

    EDIT: I missed the part about retutning AR relation.

    expect(describe_class.for_ids_with_order([order.id])).to be_a(ActiveRecord::Relation)