Search code examples
ruby-on-railsruby-on-rails-3.1rspec-rails

How do I fix undefined method `merge!' for "a-sample-blog-post":String


I have a admin namespaced controller for a simple blog I am writing, as soon as I add in some user support to my test I get back an error, this error only happens when I add the let(:user) { ... } and before { ... } bit

 Failure/Error: post :create, :post => blog_post
 NoMethodError:
   undefined method `merge!' for "a-sample-blog-post":String
 # ./app/controllers/admin/posts_controller.rb:13:in `create'
 # ./spec/controllers/admin/post_controller_spec.rb:13:in `block (4 levels) in <top (required)>'
 # ./spec/controllers/admin/post_controller_spec.rb:12:in `block (3 levels) in <top (required)>'

This is on line 13 of PostsController:

@post = Post.new(params[:post].merge!(:user => current_user))

My simple test code is as follows

require 'spec_helper'

describe Admin::PostsController do
  describe "new blog post with valid attributes" do

    let(:blog_post) { Factory(:post) }
    let(:user) { Factory(:user, :admin => true) }

    before { sign_in user }

    it "should be valid" do
      lambda do 
        post :create, :post => blog_post
      end.should change(Post, :count).by(1)
    end
  end
end

And in case this helps, here is my factories.rb file

Factory.define :post do |post|
  post.title          "A sample blog post"
  post.content        "Duis mollis, est non commodo eget lacinia odio sem nec elit."
end

Factory.define :user do |user|
  user.email "[email protected]"
  user.password "foobar"
  user.admin true
end

Can someone help me understand?


Solution

  • It's telling you what the error is:

     undefined method `merge!' for "a-sample-blog-post":String
    

    And that's only happening during the post :create, :post => blog_post section of your test which means it's directly related to creating a new Post object (and has nothing to do with the let(:user) section).

    Ruby's merge method only works on hashes, but the params you are trying to merge the :user entry into is a string which is incompatible.

    What I'm not seeing is how you're associating users with posts. For example, you should probably have a user_id field in the Post model which ties a user to a specific post (to tell you who created that post).

    So rewriting this a bit:

    class Post < ActiveRecord::Base
      # tell Rails what we should let the user update via a form
      attr_accessible :title, :content
    
      # == Schema Information
      #
      # Table name: posts
      #
      #  id         :integer         not null, primary key
      #  user_id    :integer
      #  title      :string(255)
      #  content    :text    
      #  created_at :datetime
      #  updated_at :datetime
    end
    

    In your PostsController

    def create
      @post = Post.new(params[:post])
      @post.user_id = current_user.id
    
      if @post.save
        # great success
      else
        # Y U NO SAVE?!
      end
    end
    

    And your params should include :title and :content.

    If you want to automatically associate your Factory(:post) with a user, you can do:

    Factory.define :post do |post|
      post.association    :user
      post.title          "A sample blog post"
      post.content        "Duis mollis, est non commodo eget lacinia odio sem nec elit."
    end