---
title: This should_change your mind
teaser:
tags: web,testing,shoulda
author: Josh Clayton
published_on: 2010-06-24
---

There's been a bunch of hullabaloo about our recent deprecation of the Shoulda
macros `should_change` and `should_not_change`.  A lot of people have asked,
"why remove it?"  Short answer: we don't use it anymore.  Long answer: **it's
confusing and may encourage bad programming practices**.

Consider this example of `should_change`:

    context "updating a post" do
      setup do
        @post = Post.create(:title => "old")
        put :update, :post => {:title => "new"}, :id => @post.to_param
      end

      should_change("the post title", :from => "old", :to => "new") { @post.title }
    end

This reads well and seems to be fairly straightforward.  Sadly, **this doesn't
work** because of how `should_change` works internally (hence the confusing
part).  It would actually need to be written like this:

    context "given a post" do
      setup do
        @post = Post.create(:title => "old")
      end

      context "updating" do
        setup do
          put :update, :post => {:title => "new"}, :id => @post.to_param
        end

        should_change("the post title", :from => "old", :to => "new") { @post.title }
      end
    end

The `@post` instance variable needs to be assigned a context above the context
containing `should_change`.  I _told_ you it was confusing.

Now, onto the bad practice aspect.  This tests that the post was saved!  Come
on, we don't want to do that here.  That's why we write Cucumber scenarios!
We're testing _the controller_, right?  If that's the case, then we shouldn't
care whatsoever if `update_attributes` worked with specific data passed to it,
just that it was called and that we handle the result accordingly.

    context "updating a post" do
      setup do
        @post = Factory :post
        Post.stubs(:find => @post)
      end

      context "when the update is successful" do
        setup do
          @post.stubs(:update_attributes => true)
          put :update, :post => {:title => "new"}, :id => @post.to_param
        end

        should set_the_flash.to(/updated/)
        should redirect_to(posts_path)
      end

      context "when the update is unsuccessful" do
        setup do
          @post.stubs(:update_attributes => false)
          put :update, :post => {:title => "new"}, :id => @post.to_param
        end

        should render_template(:edit)
        should assign_to(:post).with(@post)
      end
    end

This is more inline with what we _really_ want to test; that the controller
handled `update_attributes` correctly when the update worked and when it didn't.
Leave the model logic and whether it should save or not in the model (and its
tests), and test the controller to ensure it handles whether the model was
updated.

I mentioned before that Cucumber scenarios should cover overall integration.
Something like these would work:

    Scenario: Update a post successfully
      Given I am signed in
      And I authored a post titled "Great Scott"
      When I go to edit the "Great Scott" post
      And I fill in "Title" with "Willard Scott"
      And I press "Update"
      Then I should see the flash message "Your post was updated successfully"
      And I should have authored "Willard Scott"

    Scenario Example: Update a post unsuccessfully
      Given I am signed in
      And I authored a post titled "Great Scott"
      When I go to edit the "Great Scott" post
      And I fill in "<required field>" with ""
      And I press "Update"
      Then I should see error messages
      When I fill in "<required field>" with "<valid value>"
      And I press "Update"
      Then I should see "Your post was updated successfully"
      Examples:
        | required field | valid value       |
        | Title          | Awesome Post Name |
        | Body           | Awesome story     |

I'm sure this isn't the only way people were using `should_change`, but
hopefully this clarifies how deprecating the methods will encourage better
tests.

Do you use `should_change` and think it's by far the best way to test certain
pieces of code?  I'd like to see some people who'll sorely miss it post some
real code ([Pastebin](http://pastebin.com/) or [Gist](http://gist.github.com/)).
