Lots of shoulda news to report. The 2.0.x gem has been released, which includes bug fixes, new features, and deprecations.
Rails 2.1+ install
The gem is being hosted at github.com/thoughtbot/shoulda.
Specify the gem dependency in your config/environment.rb file:
Rails::Initializer.run do |config|
config.gem 'thoughtbot-shoulda', :lib => 'shoulda/rails', :source => "http://gems.github.com"
end
Then:
rake gems:install
rake gems:unpack
Now 100% gem
Why gems?
- Ruby comes with a packaging system. No need for another, especially with the gem support in Rails 2.1.
- It’s nice to scan the vendor/gems listing and see version numbers.
- github has excellent gem support.
- In shoulda’s case, it’s a project that can be used in and out of Rails. It’s a pain to have both a plugin and a gem.
Ryan McGeary and Josh Nichols did the bulk of this conversion and related refactoring. They deserve extra acclaim because they’ve made it easier for everyone to contribute to shoulda.
Improvements to current macros
should_assign_to
now takes an :equals option. This is now the preferred way to
use should_assign_to
for edit, show, update, and destroy actions because it is
a better test when you explicitly compare assigns(:user)
to an expected
object.
should_assign_to :user, :equals => "@user"
should_assign_to
also now takes a :class
option. This is especially good for
new actions.
should_assign_to :user, :class => User
should_have_one
now supports the :dependent option.
should_have_one :address, :dependent => :destroy
ActiveRecord::Errors.default_error_messages
is used instead of home-brewed
regexes for all Active Record macros.
New Macros
should_change
creates a test asserting a change between the return value of an
expression that is run before and after the current setup block is run. This is
similar to Active Support’s assert_difference
assertion, but supports more
than just numeric values.
context "Creating a post"
setup { Post.create }
should_change "Post.count", :by => 1
end
# :from and :to examples
should_change "Post.count", :from => 0, :to => 1
should_change "@post.title", :from => "old", :to => "new"
should_not_change
creates a test asserting no change between the return value
of an expression that is run before and after the current setup block is run.
context "Updating a post"
setup { @post.update_attributes(:title => "new") }
should_not_change "Post.count"
end
should_filter_params
creates a test asserting that filter_parameter_logging
is set for the specified keys should_filter_params :password, :ssn
should_render_with_layout
creates a test asserting that the controller
rendered with the given layout.
should_render_with_layout # defaults to application
should_render_with_layout 'special'
should_render_without_layout
should_route
creates a routing test. It tries to use the given HTTP method on
the given path, and asserts that it routes to the given options.
should_route :get, "/posts", :controller => :posts, :action => :index
should_route :delete, "/posts/1", :action => :destroy, :id => 1
should_route :get, "/users/1/posts/1",
:action => :show, :id => 1, :user_id => 1
should_respond_with_content_type
creates a test asserting that the response
content type was content_type
.
should_respond_with_content_type 'application/rss+xml'
should_respond_with_content_type :rss
should_respond_with_content_type /rss/
should_return_from_session
creates a test asserting that a value returned from
the session is correct. The given string is evaled to produce the resulting
redirect path. All of the instance variables set by the controller are
available to the evaled string.
should_return_from_session :user_id, "@user.id"
Before statements
Before statements are should statements that run before the current context’s setup. These are especially useful when setting expectations.
class UserControllerTest < Test::Unit::TestCase
context "the index action" do
setup do
@users = [Factory(:user)]
User.stubs(:find).returns(@users)
end
context "on GET" do
setup { get :index }
should_respond_with :success
# runs before "get :index"
before_should "find all users" do
User.expects(:find).with(:all).returns(@users)
end
end
end
end
Automatically load custom macros
Writing custom macros is a common practice for The Modern Shoulda Developer. For example, should_have_attached_file used to be a handy custom macro to have around when you’re using Paperclip.
There were a few problems with that, however. Where should that macro go?
RAILS_ROOT/test/test_helper.rb
? Why do we have the same macro in multiple apps
for the same plugin?
Enter the new shoulda_macros
directory. Shoulda will automatically load custom
macros she finds in:
RAILS_ROOT/test/shoulda_macros
RAILS_ROOT/vendor/gems/#{gem_name}/shoulda_macros
RAILS_ROOT/vendor/plugins/#{plugin_name}/shoulda_macros
Now, every Paperclip user can use the latest should_have_attached_file because it is where it belongs.
should_be_restful
is being deprecated
should_be_restful
will be removed in a future release. It is currently in a
grace period where you’ll see a warning during your test runs. It will be added
to the new woulda gem if you care to keep
using it. Its problems:
- It dissuades the programmer from good TDD practice. It’s working at too high level of abstraction and encourages the programmer to take too many big steps.
- It is an attempt to be like Rails scaffolding, which is a good way to learn REST, but the syntax is so cryptic it doesn’t accomplish that goal.
- It’s near impossible to find the line in your test file that is actually failing or erroring.
- You have no idea how many tests it generates or how they’re implemented, which lulls the programmer into a false sense of security.
Removed
- The
load_all_fixtures
(usefixtures :all
instead) - The
shoulda.conf
and color support (use the redgreen gem instead)
Credit
Ryan McGeary, Matt Jankowski, Mike Boone, Tammer Saleh, Josh Nichols, Joe Ferris, Keith Morrison, Dan Croak.