3

I have a controller create action that creates a new blog post, and runs an additional method if the post saves successfully.

I have a separate factory girl file with the params for the post I want to make. FactoryGirl.create calls the ruby create method, not the create action in my controller.

How can I call the create action from the controller in my RSpec? And how would I send it the params in my factory girl factories.rb file?

posts_controller.rb

def create
  @post = Post.new(params[:post])
  if @post.save
    @post.my_special_method
    redirect_to root_path
  else
    redirect_to new_path
  end
end

spec/requests/post_pages_spec.rb

it "should successfully run my special method" do
  @post = FactoryGirl.create(:post)
  @post.user.different_models.count.should == 1
end

post.rb

def my_special_method
  user = self.user
  special_post = Post.where("group_id IN (?) AND user_id IN (?)", 1, user.id)
  if special_post.count == 10
    DifferentModel.create(user_id: user.id, foo_id: foobar.id)
  end
end   

end

5
  • 1
    I think you're a little confused about what FactoryGirl is for... it's for generating data in your database, not for calling controller actions. And what you have described in your post_pages_spec.rb looks like a unit test on the Post model, not an actual request spec. Explicitly calling controller actions is only done in controller specs, and is done like so: relishapp.com/rspec/rspec-rails/v/2-13/docs/controller-specs Commented May 14, 2013 at 2:24
  • Thanks sevenseacat. I added the definition for my_special_method for clarity. As you can see, the method counts the number of special_posts that have been saved in the database. I have a before block in my rspec that creates 9 prior posts. I want to make sure the tenth post results in the creation of a new differentmodel object. Commented May 14, 2013 at 2:35
  • Again this is not a request spec. A controller spec is the only one that explicitly calls a controller action. Commented May 14, 2013 at 3:07
  • OK, thanks sevenseacat. I thought I read (RailsCasts, Rails Tutorial) that some people forgo controller specs and cover everything with just model and request specs. Is this bad practice? Commented May 14, 2013 at 3:37
  • 1
    Some people do, but that doesn't mean you just put controller tests inside a request spec. Commented May 14, 2013 at 3:44

1 Answer 1

5

Request specs are integration tests, using something like Capybara to visit pages as a user might and perform actions. You wouldn't test a create action from a request spec at all. You'd visit the new item path, fill in the form, hit the Submit button, and then confirm that an object was created. Take a look at the Railscast on request specs for a great example.

If you want to test the create action, use a controller spec. Incorporating FactoryGirl, that would look like this:

it "creates a post" do
  post_attributes = FactoryGirl.attributes_for(:post)
  post :create, post: post_attributes
  response.should redirect_to(root_path)
  Post.last.some_attribute.should == post_attributes[:some_attribute]
  # more lines like above, or just remove `:id` from
  #   `Post.last.attributes` and compare the hashes.
end

it "displays new on create failure" do
  post :create, post: { some_attribute: "some value that doesn't save" }
  response.should redirect_to(new_post_path)
  flash[:error].should include("some error message")
end

These are the only tests you really need related to creation. In your specific example, I'd add a third test (again, controller test) to ensure that the appropriate DifferentModel record is created.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Jim, this is perfect. By the way, I also want to test a voting action, to a path of vote_post_path(post, type: "like"). How would I do that? I've tried post :vote, post: my_post, type: "like", but this gives me No route matches {:post=>"391", :type=>"like", :controller=>"post", :action=>"vote"} rake routes tells me its vote_post POST /posts/:id/vote(.:format)
nevermind, figured it out. post :vote, id: my_post.id, type: 'like' worked fine. Thanks again for your help.

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.