As someone relatively new to rspec, I am interested in hearing the wisdom of the group in the area of using a fixture replacement gem (such as machinist or factory girl) instead of mocking the model. To me it seems that using a fixture replacement, instead of a mock, would circumvent some of rspec''s framework in a bad way. Or perhaps there are certain places where a fixture replacement can be beneficial, and certain places where only a mock will do? Do many of you guys in the group use a fixture replacement?
On 1 Sep 2009, at 21:11, Jeremy Hageman wrote:> As someone relatively new to rspec, I am interested in hearing the > wisdom of the group in the area of using a fixture replacement gem > (such as machinist or factory girl) instead of mocking the model. To > me it seems that using a fixture replacement, instead of a mock, would > circumvent some of rspec''s framework in a bad way. Or perhaps there > are certain places where a fixture replacement can be beneficial, and > certain places where only a mock will do? > > Do many of you guys in the group use a fixture replacement?Yes, I do. My advice is: use mocks when you can, fixture replacements when you must. Tests using mocks are much faster (and therefore fun) to run, but sometimes ActiveRecord makes it pretty hard for you to separate the persistable behaviour of your model class from the business logic, meaning it''s simpler (and therefore more fun) just to give it up and hit the database. Fixture replacements are also invaluable for writing acceptance tests in Cucumber. cheers, Matt
On Tue, Sep 1, 2009 at 4:11 PM, Jeremy Hageman<jjhageman at gmail.com> wrote:> As someone relatively new to rspec, I am interested in hearing the > wisdom of the group in the area of using a fixture replacement gem > (such as machinist or factory girl) instead of mocking the model.For what kind of test? -- Have Fun, Steve Eley (sfeley at gmail.com) ESCAPE POD - The Science Fiction Podcast Magazine http://www.escapepod.org
Jeremy Hageman wrote:> As someone relatively new to rspec, I am interested in hearing the > wisdom of the group in the area of using a fixture replacement gem > (such as machinist or factory girl) instead of mocking the model. To > me it seems that using a fixture replacement, instead of a mock, would > circumvent some of rspec''s framework in a bad way. Or perhaps there > are certain places where a fixture replacement can be beneficial, and > certain places where only a mock will do? > > Do many of you guys in the group use a fixture replacement? >As the author of FixtureReplacement (which I guess coined the term), I use one (it) all of the time. Rails / AR is heavily centered around removing abstraction layers from the database - as such, most testing involves the database. Any one who has used mocking in this arena knows how brittle mocks in a rails app can become. Mocking is used outside of rails for two reasons. The first is as a design tool. The idea behind that is that by defining the interfaces before the details you''ll end up with a cleaner, more intuitve interface. The second is where it is necessary to do so, e.g., external API''s etc. But (full) mocking comes with a price - brittle specs. David C. has suggested in the past that after the implementation is complete, mocks could (and often should) be replaced with the real objects. Most mocks used in a rails project server neither of these purposes. Primarily they are used for speed - which so far has been the primary trade off with using a fixture replacement (such as FixtureReplacement, FactoryGirl, etc). Those who advocate mocking all find the need for a higher level coverage tool, such as cucumber, etc. Either way (until a better solution, such as guillotine becomes available), you''ll end up running a slow-ish test suite. Regards, Scott
Scott thanks for the insight. On Sep 1, 9:16?pm, Scott Taylor wrote:> Most mocks used in a rails project server neither of these purposes. ? > Primarily they are used for speed - which so far has been the primary > trade off with using a fixture replacement (such as FixtureReplacement, > FactoryGirl, etc).As someone who sound like they rely heavily on a fixture replacement, do you find you achieve the same level of isolation from dependencies when setting up your tests? I personally feel I run across more errors setting up my specs when I am primarily using a fixture replacement, as opposed to mocks. Though I realize this is probably do to a learning curve involved with using specs and a fixture replacement in harmony.
On Sep 1, 3:26 pm, Stephen Eley wrote:> For what kind of test?The specific situation that started the question rolling around in my head was specing out a build method in a controller which creates an instance of a join model between two User instances. My setup includes AuthLogic and Machinist. I am using the suggested AuthLogic setup which has a current_user helper method in the app which returns the current user from the session. One of my main reasons for using a fixture replacement is because I can create a user object with Machinist and then set the user as logged in. To me this more closely models the structure of the app. But as someone new to rspec, I found it tricky to spec out the build method in the following controller code. Which leads me to believe I was using fixture replacements inappropriately and/or too often. # follows_controller.rb class FollowsController < ApplicationController def create @follow = current_user.follows.build(:followed_id => params [:followed_id]) if @follow.save flash[:notice] = "Following created." redirect_to user_path(params[:followed_id]) else flash[:error] = "Unable to follow." redirect_to user_path(params[:followed_id]) end end end Initially, I wanted to write expectations such as: current_user.follows.should_receive(:build) But this is not possible since current_user is a method unavailable to the specs. At this point I realize this situation is much better served by stubbing and mocking the functions and models.
On Wed, Sep 2, 2009 at 3:34 AM, Jeremy Hageman<jjhageman at gmail.com> wrote:> > But this is not possible since current_user is a method unavailable to > the specs. At this point I realize this situation is much better > served by stubbing and mocking the functions and models.Why isn''t current_user available to your controller specs? If you defined it in ApplicationController, and your controller inherits from that, then it''s just another method on your controller. You may or may not want to stub it for convenience, to return a mock or a factory model, but it should be accessible either way. As for which to use: I''ve done both, and at this moment I''m not sure I could pin down a rhyme or reason for you. It''s whatever seems easier given my current thinking at the time, or sometimes doing neither. The important thing isn''t really "how do I fake out the stuff that I''m *not* testing in this spec," but rather "why am I testing this code?" (i.e. "what do I think might break here?") and "what do I need to do to establish that this code works or doesn''t work?" Mocks and factories are just details to get you there. -- Have Fun, Steve Eley (sfeley at gmail.com) ESCAPE POD - The Science Fiction Podcast Magazine http://www.escapepod.org
On Sep 2, 6:06?am, Stephen Eley wrote:> Why isn''t current_user available to your controller specs? ?If you > defined it in ApplicationController, and your controller inherits from > that, then it''s just another method on your controller. ?You may or > may not want to stub it for convenience, to return a mock or a factory > model, but it should be accessible either way.Thanks for the insight. After poking around a little with debugger I can see that the controller has access to current_user, but the spec tests do not. So I copied the helper methods into a module and included them in the spec_helper. Now the tests have access to current_user. However, I still can''t seem to get a should_receive expectation work for the build method without mocking everything out. Any ideas on what I''m missing. describe FollowsController do before(:each) do @user = User.make login_as(@user) end describe "POST create" do it "should build a follow" do current_user.follows.should_receive(:build) post :create, :followed_id => "999" end end end Spec::Mocks::MockExpectationError in ''FollowsController POST create should build a follow'' expected :build with (any args) once, but received it 0 times
Jeremy Hageman wrote:> Scott thanks for the insight. > > On Sep 1, 9:16 pm, Scott Taylor wrote: > >> Most mocks used in a rails project server neither of these purposes. >> Primarily they are used for speed - which so far has been the primary >> trade off with using a fixture replacement (such as FixtureReplacement, >> FactoryGirl, etc). >> > > As someone who sound like they rely heavily on a fixture replacement, > do you find you achieve the same level of isolation from dependencies > when setting up your tests?As long as the (dependent) factory methods are generating valid objects (they will raise as they use create! and save! internally if they aren''t), you should see exactly what you would from an invalid AR object. I usually run one test file at a time, too, which helps with the error count with dependencies, and only later when the class under test is passing, I''ll go and run the whole suite. Autotest does the same thing if you don''t like to do it manually. Scott