How to write an RSpec test for a simple PUT update?

40,642

Solution 1

You forgot to .reload your @article, and on update action your response most likely perform redirect, so

RSpec 2:

describe "PUT update/:id" do
  let(:attr) do 
    { :title => 'new title', :content => 'new content' }
  end

  before(:each) do
    put :update, :id => @article.id, :article => attr
    @article.reload
  end

  it { response.should redirect_to(@article) }
  it { @article.title.should eql attr[:title] }
  it { @article.content.should eql attr[:content] }
end

Rspec 3:

describe "PUT update/:id" do
  let(:attr) do 
    { :title => 'new title', :content => 'new content' }
  end

  before(:each) do
    put :update, :id => @article.id, :article => attr
    @article.reload
  end

  it { expect(response).to redirect_to(@article) }
  it { expect(@article.title).to eql attr[:title] }
  it { expect(@article.content).to eql attr[:content] }
end

Solution 2

When you are doing a PUT :update remember that you are editing an existing model, which you need to call in the put. Just pass your @article and update the attributes as follows.

describe "PUT 'update/:id'" do
  it "allows an article to be updated" do
    put :update, :id => @article.id, :article => @article.attributes = { :title => "new title", :content => "new content" }
    response.should be_successful
  end
end

Solution 3

FactoryGirl.define :article do
  title "a title"
  content "hello world"
end

before(:each) do
  @article = Factory(:article)
end

it "should re-render edit template on failed update" do
  @attr = { :title => "", :content => "new content" }
  put :update, :id => @article.id, :article => @attr

  flash[:notice].should be_nil
  response.should render_template('edit')
end

it "should redirect to index with a notice on successful update" do
  @attr = { :title => "new title", :content => "new content" }
  put :update, :id => @article.id, :article => @attr

  assigns[:article].should_not be_new_record
  flash[:notice].should_not be_nil
  response.should redirect_to(:action => 'index')
end
Share:
40,642
oort
Author by

oort

Updated on July 19, 2022

Comments

  • oort
    oort almost 2 years

    I'm trying to solidify my understanding of rails and the BDD workflow, so I wanted to start small by creating one of those mini-blogs, but with rspec. Right now I have an ArticlesController and Article model, and associated rspec files. Article is very simple, has just title:string and content:text, and the ArticlesController is RESTful - although I hand wrote the MCV for Article, it's basically the same as if I used a scaffold to create it.

    However I don't really know what I'm doing when it comes to writing a test in rspec for the PUT update. I'm using Factory Girl to create the article object, and so far my code looks like:

    #factories.rb
    FactoryGirl.define do
      factory :article do
      title "a title"
      content "hello world"
    end
    
    #articles_controller_spec.rb
    before(:each) do
      @article = Factory(:article)
    end
    
    describe "PUT 'update/:id'" do
      it "allows an article to be updated" do
        @attr = { :title => "new title", :content => "new content" }
        put :update, :id => @article.id, :article => @attr
        response.should be_successful
      end
    end
    

    However I keep getting:

    Failures:
    1) ArticlesController PUT 'update/:id' allows an article to be updated
       Failure/Error: response.should be_successful
         expected successful? to return true, got false
    

    What am I doing wrong? And am I using the right tools? When I run my test server, New, Edit, Destroy all work as I would expect them to, so I'm guessing this is a problem with my understanding of RSpec. Let me know if I'm wrong - thanks!

  • engineerDave
    engineerDave almost 11 years
    also the new matcher syntax would have this as @article.title.should eq @attr[:title]
  • KnownColor
    KnownColor over 10 years
    You code seems to assert the times are the same, not different as you describe.
  • Sandip Ransing
    Sandip Ransing over 10 years
    @KnownColor by default update is put request but you can always override it.
  • Matt
    Matt about 10 years
    Bless your beautiful little soul. I was at my wits end.
  • Nick
    Nick almost 10 years
    The newer syntax should be: expect(@article.title).to eq @attr[:title].
  • wh1tney
    wh1tney over 9 years
    I was forgetting reload after updating. This answer saved me. Thank you!
  • yekta
    yekta over 8 years
    From the rails conf 2014, I learned that using instance methods in specs is not a good idea. It'd be nice to see an example here without instance methods.
  • rovitulli
    rovitulli over 7 years
    put :update, params: { id: @article.id, article: attr } In case you face 'ActionController::TestCase HTTP request methods will accept only keyword arguments in future Rails versions.' deprecation warning.