Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to use `FactoryGirl.build_stubbed` and where to use RSpec's `mock`/`double`

Tags:

I'm just curious where people tend to use FactoryGirl.build_stubbed and where they use double when writing RSpec specs. That is, are there best practices like "only use FactoryGirl methods in their corresponding model specs?"

Is it a code smell when you find yourself using FactoryGirl.create(:foo) in spec/models/bar_spec.rb?

Is it less of a code smell if you're using FactoryGirl.build_stubbed(:foo) in spec/models/bar_spec.rb?

Is it a code smell if you're using FactoryGirl.create(:foo) in foos_controller_spec.rb?

Is it less of a code smell if you're using FactoryGirl.build_stubbed(:foo) in foos_controller_spec.rb?

Is it a code smell if you're using FactoryGirl.build_stubbed(:foo) in spec/decorators/foo_decorator_spec.rb?

Sorry for so many questions! I just would love to know how other people draw the lines in unit test isolation and object oriented design best practices.

Thanks!

like image 997
Aaron Gibralter Avatar asked May 31 '12 15:05

Aaron Gibralter


People also ask

What is Build_stubbed?

build_stubbed is the younger, more hip sibling to build ; it instantiates and assigns attributes just like build , but that's where the similarities end. All reactions. Sign up for free to join this conversation on GitHub.


1 Answers

I believe that there are best practices that guide us to think about when to use mocks (in this case "doubles") versus integrating against real dependencies (in this case "Factories"). There is a really good book on testing (caveat: it uses Java examples) that describes the purpose of test-driven development, and I think it's very helpful in this discussion on testing in Rails applications. It describes the intention of testing as follows:

... our intention in test-driven development is to use mock objects to bring out relationships between objects.

Freeman, Steve; Pryce, Nat (2009-10-12). Growing Object-Oriented Software, Guided by Tests (Kindle Locations 3878-3879). Pearson Education (USA). Kindle Edition.

If we think about this emphasis on using test-driven development not only to prevent us from introducing regressions, but to help us think about how our code is structured in terms of its interface and relationships to other objects we will naturally use mocks in many cases. I'll describe how this applies to your specific questions below.

First, in terms of whether we use mock objects or real dependencies in model tests - if we're testing class Foo and its dependency on Bar, we may want to substitute a mock for Bar. In this way we will see clearly the level of coupling to Bar as we'll have to mock the methods that will be called on it. If we find that our mock of Bar is complex, it's a sign that perhaps we should refactor Foo and Bar so that they are less coupled to one another.

In the sense that both Factory.create and Factory.build_stubbed have the same effect of keeping you from making dependencies on related classes explicit, I think they're both about as smelly, with Factory.create being the slower of the two options.

In my tests I tend to not worry too much about mocking external dependencies in controllers. I know that this is slower to run than fully mocking, and you don't have the benefit of making the controller coupling to the model explicit, but it's quicker to write the test, and I'm not generally as worried about making clear the relationship between controllers and the persisted records that they manage. As long as you follow patterns of "skinny controllers" there shouldn't be too logic to worry about here anyway. If we need to specify a level of "test smell" here I would say that it's a bit less smelly than model tests that depend on other factories.

I would tend to worry least about Decorators that depend on factories of the classes that they decorate. This is because by definition the decorator should maintain the same interface as the class they decorate. Decoration is most often implemented with some form of inheritance, whether by using method_missing to delegate to the decoratee, or by explicit subclassing of the decoratee. Because of this you're breaking other rules of good object-oriented programming like Liskov Substitution if the decorator deviates too much from the interface of the thing it decorates. As long as your decoration isn't implemented poorly by breaking rules of good inheritance, the coupling to the class that you decorate is already present, so it's not making things much worse if you have the test of the decoratee depend on a persisted or stubbed factory of the thing that it decorates. You can go crazy with factories in decorator tests and it doesn't matter IMO.

I think that it's important to note that even if you prefer mocks in most cases you should still have some integration tests that use real dependencies. You'll just find that these cover specific high-value cases, where the isolated unit tests provide more coverage of functionality provided by your classes.

At any rate I break all of the above rules sometimes and they are just some guidelines that I use in writing tests. I'm looking forward to hearing how others are using Factories (build_stubbed and really persisted) versus mock objects (doubles) in their tests.

like image 88
Justin Leitgeb Avatar answered Nov 12 '22 23:11

Justin Leitgeb