mercredi 30 décembre 2015

rails unit testing with ActiveRecord associasions without hitting the DB

TL;DR I'd like to know if I can test model methods that use querying (i.e find, where) without persisting test objects to the database.

So I'm new to rails, and working on an existing codebase.
One thing I've noticed is that our unit tests take forever to run.
Upon investigation, the culprit was, of course, that we persist everything to DB when we test our models.
So, I set out to try and write model tests that don't hit the DB, but I've come across a snag:
When a model has an association with other models, any operations that are performed on it assume that everything is persisted.

Let's take a look at an example-

class Parent < ActiveRecord::Base
  has_many :children, dependent: :destroy

  def default_child
    children.find_by(default: true)
  end

end

So naturally I want to test that my default_child method is working:

parent = Parent.new
default_child = parent.children.build(default: true)

assert_equal default_child, parent.default_child

but this test fails, because the actual result of parent.default_child is nil!
This is because internally, the default_child method uses find_by, which, it seems, only works in the context of persisted objects.

So I'm forced to write the test like so-

parent = Parent.new
# I don't want to do this!!
parent.save
# why 'create' and not 'build'?
default_child = parent.children.create(default: true)

assert_equal default_child, parent.default_child

Which is uglier and slower.
Is there any way I can test these operations in memory?

I've tried setting children manually (parent.children = [ child1, child2 ]).
This does not cause an error, but it seems that find_by doesn't look there, but rather in the DB...

I see that a similar question came up 3 years ago, without a conclusive answer, I'm wondering if anything's changed since then..

P.S. bonus question- what can I do about validations which are on: update? seems like I have to persist the test object at least once before they are invoked..

Aucun commentaire:

Enregistrer un commentaire