Search code examples
rubyrspecruby-on-rails-3.2mass-assignment

Rspec - post - MassAssignmentSecurity::Error


I have few models: User and Article. User has_many articles and Article belongs to user.

I try to write test to check Post method (NOTE: i use locales in my application):

require 'spec_helper'

describe ArticlesController do
  render_views

  before(:each) do
    @admin = FactoryGirl.create( :admin )
    @user  = FactoryGirl.create( :user )
  end

  describe "POST 'create'" do
    before(:each){ @article = FactoryGirl.create(:article) }

    describe "for signed-in admin" do
      before(:each){ test_sign_in( @admin ) }

      it "should create article" do
        expect do
          post :create, :locale => :en, :article => @article.attributes.merge( :content => "some" )
        end.should change( Article, :count ).by(1)
      end
    end
  end
end

But i get such error:

1) ArticlesController POST 'create' for signed-in admin should create article
     ActiveModel::MassAssignmentSecurity::Error:
       Can't mass-assign protected attributes: id, user_id, created_at, updated_at
     Failure/Error: post :create, :locale => :en, :article => @article.attributes.merge( :content => "some" )
     # ./app/controllers/articles_controller.rb:15:in `create'
     # ./spec/controllers/articles_controller_spec.rb:106:in `block (5 levels) in <top (required)>'
     # ./spec/controllers/articles_controller_spec.rb:105:in `block (4 levels) in <top (required)>'

How can i fix that?

My Factories:

FactoryGirl.define do
  factory :user do
    sequence(:email) { |n| "email#{n}@factory.com" }
    password  'qwerty'

    factory :admin do
      admin true
    end
  end

  factory :article do
    content 'text is here'
    user
  end
end

My controller:

class ArticlesController < ApplicationController
  before_filter do
    redirect_to :root unless current_user && current_user.admin?
  end

  def create
    @article = Article.new( params[:article] )

    if @article.save
      redirect_to articles_path
      flash[:success] = "It has been created!"
    else
      render 'new'                                                                       
    end
  end
end

Article model:

# == Schema Information
#
# Table name: articles
#
#  id         :integer          not null, primary key
#  user_id    :integer
#  content    :text
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class Article < ActiveRecord::Base
  belongs_to :user

  attr_accessible :content

  validates :content,
              :presence => { :message => :presense_message },
              :length   => {
                             :maximum => 50000,
                             :message => :max_lenght_message
                           }
end

UPD: Application is here: https://github.com/Loremaster/Chirch_app


Solution

  • Without testing your code directly I can't say for sure, but try changing

    before(:each){ @article = FactoryGirl.create(:article) } 
    

    to

    before(:each){ @article = FactoryGirl.build(:article, :user => @admin) } 
    

    so that you are giving the article an author as well as not saving the article before you make the call to POST. Also, this line should probably also go within your describe "for signed-in admin" block.

    Edit

    Your MassAssignmentSecurity::Errors will go away if you change your test code so that you build your :article upfront with all the necessary parameters as defined in your factories:

    spec/controllers/articles_controllers.spec

    describe "POST 'create'" do
      before(:each) do
        @article = FactoryGirl.build(:article, :user => @admin, :content => "some")
      end
    
    describe "for non-signed users" do
      it "should deny access" do
        post :create, :locale => :en, :article => @article
        response.should redirect_to( root_path )
      end
    
      it "should not create article" do
        expect do
          post :create, :locale => :en, :article => @article
        end.should_not change( Article, :count )
      end
    end
    
    describe "for signed-in users" do
      before(:each){ test_sign_in( @user ) }
    
      it "should deny access" do
        post :create, :locale => :en, :article => @article
        response.should redirect_to( root_path )
      end
    
      it "should not create article" do
        expect do
          post :create, :locale => :en, :article => @article
        end.should_not change( Article, :count )
      end
    end