diff --git a/README b/README
index 192fd54c..3632e5cb 100644
--- a/README
+++ b/README
@@ -1,17 +1,123 @@
-= ThoughtBot Test Helpers
+= Shoulda
-A collection of Test::Unit helper methods.
+Shoulda makes it easy to write elegant, understandable, and maintainable tests. Shoulda consists of test macros, assertions, and helpers added on to the Test::Unit framework. It's fully compatible with your existing tests, and requires no retooling to use.
-Adds helpers for
+Helpers:: #context and #should give you rSpec like test blocks.
+ In addition, you get nested contexts and a much more readable syntax.
+Macros:: Generate hundreds of lines of Controller and ActiveRecord tests with these powerful macros.
+ They get you started quickly, and can help you ensure that your application is conforming to best practices.
+Assertions:: Many common rails testing idioms have been distilled into a set of useful assertions.
-* context and should statements
-* Common ActiveRecord model tests
-* A few general purpose assertions
+= Usage
+
+=== Context Helpers (ThoughtBot::Shoulda::Context)
+
+Stop killing your fingers with all of those underscores... Name your tests with plain sentences!
+
+ class UserTest << Test::Unit
+ context "A User instance" do
+ setup do
+ @user = User.find(:first)
+ end
+
+ should "return its full name"
+ assert_equal 'John Doe', @user.full_name
+ end
+
+ context "with a profile" do
+ setup do
+ @user.profile = Profile.find(:first)
+ end
+
+ should "return true when sent #has_profile?"
+ assert @user.has_profile?
+ end
+ end
+ end
+ end
+
+Produces the following test methods:
+
+ "test: A User instance should return its full name."
+ "test: A User instance with a profile should return true when sent #has_profile?."
+
+So readable!
+
+=== ActiveRecord Tests (ThoughtBot::Shoulda::ActiveRecord)
+
+Quick macro tests for your ActiveRecord associations and validations:
+
+ class PostTest < Test::Unit::TestCase
+ load_all_fixtures
+
+ should_belong_to :user
+ should_have_many :tags, :through => :taggings
+
+ should_require_unique_attributes :title
+ should_require_attributes :body, :message => /wtf/
+ should_require_attributes :title
+ should_only_allow_numeric_values_for :user_id
+ end
+
+ class UserTest < Test::Unit::TestCase
+ load_all_fixtures
+
+ should_have_many :posts
+
+ should_not_allow_values_for :email, "blah", "b lah"
+ should_allow_values_for :email, "a@b.com", "asdf@asdf.com"
+ should_ensure_length_in_range :email, 1..100
+ should_ensure_value_in_range :age, 1..100
+ should_protect_attributes :password
+ end
+
+Makes TDD so much easier.
+
+=== Controller Tests (ThoughtBot::Shoulda::Controller::ClassMethods)
+
+Macros to test the most common controller patterns...
+
+ context "on GET to :show for first record" do
+ setup do
+ get :show, :id => 1
+ end
-== Todo
+ should_assign_to :user
+ should_respond_with :success
+ should_render_template :show
+ should_not_set_the_flash
-* Many more tests (of the tests). Specifically, the ActiveRecord macros need to be tested.
-* Controller test helpers
-* General code cleanups
-* More options for AR helpers
-
\ No newline at end of file
+ should "do something else really cool" do
+ assert_equal 1, assigns(:user).id
+ end
+ end
+
+Test entire controllers in a few lines...
+
+ class PostsControllerTest < Test::Unit::TestCase
+ should_be_restful do |resource|
+ resource.parent = :user
+
+ resource.create.params = { :title => "first post", :body => 'blah blah blah'}
+ resource.update.params = { :title => "changed" }
+ end
+ end
+
+should_be_restful generates 40 tests on the fly, for both html and xml requests.
+
+=== Helpful Assertions (ThoughtBot::Shoulda::General)
+
+More to come here, but have fun with what's there.
+
+ load_all_fixtures
+ assert_same_elements([:a, :b, :c], [:c, :a, :b])
+ assert_contains(['a', '1'], /\d/)
+ assert_contains(['a', '1'], 'a')
+
+= Credits
+
+Shoulda is maintained by {Tammer Saleh}[mailto:tsaleh@thoughtbot.com], and is funded by Thoughtbot[http://www.thoughtbot.com], inc.
+
+= License
+
+Shoulda is Copyright © 2006-2007 Tammer Saleh, Thoughtbot. It is free software, and may be redistributed under the terms specified in the README file of the Ruby distribution.
\ No newline at end of file
diff --git a/lib/shoulda.rb b/lib/shoulda.rb
index b72c27d7..a549e0b9 100644
--- a/lib/shoulda.rb
+++ b/lib/shoulda.rb
@@ -22,9 +22,9 @@ end
require 'shoulda/color' if shoulda_options[:color]
-module Test # :nodoc:
- module Unit # :nodoc:
- class TestCase # :nodoc:
+module Test # :nodoc: all
+ module Unit
+ class TestCase
include ThoughtBot::Shoulda::Controller
include ThoughtBot::Shoulda::General
@@ -38,10 +38,10 @@ module Test # :nodoc:
end
end
-module ActionController #:nodoc:
- module Integration #:nodoc:
- class Session #:nodoc:
- include ThoughtBot::Shoulda::General::InstanceMethods
+module ActionController #:nodoc: all
+ module Integration
+ class Session
+ include ThoughtBot::Shoulda::General
end
end
end
diff --git a/lib/shoulda/color.rb b/lib/shoulda/color.rb
index 0a1a777e..1ccfad2b 100644
--- a/lib/shoulda/color.rb
+++ b/lib/shoulda/color.rb
@@ -3,7 +3,7 @@ require 'test/unit/ui/console/testrunner'
# Completely stolen from redgreen gem
#
# Adds colored output to your tests. Specify color: true in
-# your test/shoulda.conf file to enable.
+# your ~/.shoulda.conf file to enable.
#
# *Bug*: for some reason, this adds another line of output to the end of
# every rake task, as though there was another (empty) set of tests.
diff --git a/lib/shoulda/context.rb b/lib/shoulda/context.rb
index 753963f3..12d820e3 100644
--- a/lib/shoulda/context.rb
+++ b/lib/shoulda/context.rb
@@ -2,9 +2,9 @@ module ThoughtBot # :nodoc:
module Shoulda # :nodoc:
# = context and should blocks
#
- # A context block can exist next to normal def test_blah statements,
- # meaning you do not have to fully commit to the context/should syntax in a test file. We have been
- # using this syntax at ThoughtBot, though, and find it very readable.
+ # A context block groups should statements under a common setup/teardown method.
+ # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
+ # and readability of your test code.
#
# A context block can contain setup, should, should_eventually, and teardown blocks.
#
@@ -20,7 +20,7 @@ module ThoughtBot # :nodoc:
# end
# end
#
- # This code will produce the method "test a User instance should return its full name" (yes, with spaces in the name).
+ # This code will produce the method "test a User instance should return its full name".
#
# Contexts may be nested. Nested contexts run their setup blocks from out to in before each test.
# They then run their teardown blocks from in to out after each test.
@@ -48,8 +48,11 @@ module ThoughtBot # :nodoc:
# end
#
# This code will produce the following methods
- # * "test a User instance should return its full name"
- # * "test a User instance with a profile should return true when sent :has_profile?" (which will have both setup blocks run before it.)
+ # * "test: a User instance should return its full name."
+ # * "test: a User instance with a profile should return true when sent :has_profile?."
+ #
+ # A context block can exist next to normal def test_the_old_way; end tests,
+ # meaning you do not have to fully commit to the context/should syntax in a test file.
#
module Context
@@ -59,32 +62,20 @@ module ThoughtBot # :nodoc:
@@teardown_blocks = []
end
- # Creates a context block with the given name.
- def context(name, &context_block)
- saved_setups = @@setup_blocks.dup
- saved_teardowns = @@teardown_blocks.dup
- saved_contexts = @@context_names.dup
-
- @@context_names << name
- context_block.bind(self).call
-
- @@context_names = saved_contexts
- @@setup_blocks = saved_setups
- @@teardown_blocks = saved_teardowns
- end
-
- # Run before every should block in the current context
- def setup(&setup_block)
- @@setup_blocks << setup_block
- end
-
- # Run after every should block in the current context
- def teardown(&teardown_block)
- @@teardown_blocks << teardown_block
- end
-
- # Defines a test. Can be called either inside our outside of a context.
- # Optionally specify :unimplimented => true (see should_eventually)
+ # Defines a test method. Can be called either inside our outside of a context.
+ # Optionally specify :unimplimented => true (see should_eventually).
+ #
+ # Example:
+ #
+ # class UserTest << Test::Unit
+ # should "return first user on find(:first)"
+ # assert_equal users(:first), User.find(:first)
+ # end
+ # end
+ #
+ # Would create a test named
+ # 'test: should return first user on find(:first)'
+ #
def should(name, opts = {}, &should_block)
test_name = ["test:", @@context_names, "should", "#{name}. "].flatten.join(' ').to_sym
@@ -112,8 +103,38 @@ module ThoughtBot # :nodoc:
end
end
+ # Creates a context block with the given name.
+ def context(name, &context_block)
+ saved_setups = @@setup_blocks.dup
+ saved_teardowns = @@teardown_blocks.dup
+ saved_contexts = @@context_names.dup
+
+ @@context_names << name
+ context_block.bind(self).call
+
+ @@context_names = saved_contexts
+ @@setup_blocks = saved_setups
+ @@teardown_blocks = saved_teardowns
+ end
+
+ # Run before every should block in the current context.
+ # If a setup block appears in a nested context, it will be run after the setup blocks
+ # in the parent contexts.
+ def setup(&setup_block)
+ @@setup_blocks << setup_block
+ end
+
+ # Run after every should block in the current context.
+ # If a teardown block appears in a nested context, it will be run before the teardown
+ # blocks in the parent contexts.
+ def teardown(&teardown_block)
+ @@teardown_blocks << teardown_block
+ end
+
# Defines a specification that is not yet implemented.
# Will be displayed as an 'X' when running tests, and failures will not be shown.
+ # This is equivalent to:
+ # should(name, {:unimplemented => true}, &block)
def should_eventually(name, &block)
should("eventually #{name}", {:unimplemented => true}, &block)
end
diff --git a/lib/shoulda/controller_tests/controller_tests.rb b/lib/shoulda/controller_tests/controller_tests.rb
index c9085df6..ddf05807 100644
--- a/lib/shoulda/controller_tests/controller_tests.rb
+++ b/lib/shoulda/controller_tests/controller_tests.rb
@@ -1,6 +1,5 @@
module ThoughtBot # :nodoc:
module Shoulda # :nodoc:
- # = Macro test helpers for your controllers
module Controller
def self.included(other) # :nodoc:
other.class_eval do
@@ -12,6 +11,30 @@ module ThoughtBot # :nodoc:
end
end
+ # = Macro test helpers for your controllers
+ #
+ # By using the macro helpers you can quickly and easily create concise and easy to read test suites.
+ #
+ # This code segment:
+ # context "on GET to :show for first record" do
+ # setup do
+ # get :show, :id => 1
+ # end
+ #
+ # should_assign_to :user
+ # should_respond_with :success
+ # should_render_template :show
+ # should_not_set_the_flash
+ #
+ # should "do something else really cool" do
+ # assert_equal 1, assigns(:user).id
+ # end
+ # end
+ #
+ # Would produce 5 tests for the +show+ action
+ #
+ # Furthermore, the should_be_restful helper will create an entire set of tests which will verify that your
+ # controller responds restfully to a variety of requested formats.
module ClassMethods
# Formats tested by #should_be_restful. Defaults to [:html, :xml]
VALID_FORMATS = Dir.glob(File.join(File.dirname(__FILE__), 'formats', '*')).map { |f| File.basename(f, '.rb') }.map(&:to_sym) # :doc:
@@ -20,26 +43,108 @@ module ThoughtBot # :nodoc:
# Actions tested by #should_be_restful
VALID_ACTIONS = [:index, :show, :new, :edit, :create, :update, :destroy] # :doc:
+ # A ResourceOptions object is passed into should_be_restful in order to configure the tests for your controller.
+ #
+ # Example:
+ # class UsersControllerTest < Test::Unit::TestCase
+ # load_all_fixtures
+ #
+ # def setup
+ # ...normal setup code...
+ # @user = User.find(:first)
+ # end
+ #
+ # should_be_restful do |resource|
+ # resource.identifier = :id
+ # resource.klass = User
+ # resource.object = :user
+ # resource.parent = []
+ # resource.actions = [:index, :show, :new, :edit, :update, :create, :destroy]
+ # resource.formats = [:html, :xml]
+ #
+ # resource.create.params = { :name => "bob", :email => 'bob@bob.com', :age => 13}
+ # resource.update.params = { :name => "sue" }
+ #
+ # resource.create.redirect = "user_url(@user)"
+ # resource.update.redirect = "user_url(@user)"
+ # resource.destroy.redirect = "users_url"
+ #
+ # resource.create.flash = /created/i
+ # resource.update.flash = /updated/i
+ # resource.destroy.flash = /removed/i
+ # end
+ # end
+ #
+ # Whenever possible, the resource attributes will be set to sensible defaults.
+ #
class ResourceOptions
+ # Configuration options for the create, update, destroy actions under should_be_restful
class ActionOptions
- # String eval'd to get the target of the redirection
+ # String evaled to get the target of the redirection.
+ # All of the instance variables set by the controller will be available to the
+ # evaled code.
+ #
+ # Example:
+ # resource.create.redirect = "user_url(@user.company, @user)"
+ #
+ # Defaults to a generated url based on the name of the controller, the action, and the resource.parents list.
attr_accessor :redirect
- # String or Regexp describing a value expected in the flash
+ # String or Regexp describing a value expected in the flash. Will match against any flash key.
+ #
+ # Defaults:
+ # destroy:: /removed/
+ # create:: /created/
+ # update:: /updated/
attr_accessor :flash
- # Hash describing the params that should be sent in with this action
+ # Hash describing the params that should be sent in with this action.
attr_accessor :params
-
- # Actions that should be denied (only used by resource.denied)
+ end
+
+ # Configuration options for the denied actions under should_be_restful
+ #
+ # Example:
+ # context "The public" do
+ # setup do
+ # @request.session[:logged_in] = false
+ # end
+ #
+ # should_be_restful do |resource|
+ # resource.parent = :user
+ #
+ # resource.denied.actions = [:index, :show, :edit, :new, :create, :update, :destroy]
+ # resource.denied.flash = /get outta here/i
+ # resource.denied.redirect = 'new_session_url'
+ # end
+ # end
+ #
+ class DeniedOptions
+ # String evaled to get the target of the redirection.
+ # All of the instance variables set by the controller will be available to the
+ # evaled code.
+ #
+ # Example:
+ # resource.create.redirect = "user_url(@user.company, @user)"
+ attr_accessor :redirect
+
+ # String or Regexp describing a value expected in the flash. Will match against any flash key.
+ #
+ # Example:
+ # resource.create.flash = /created/
+ attr_accessor :flash
+
+ # Actions that should be denied (only used by resource.denied). Note that these actions will
+ # only be tested if they are also listed in +resource.actions+
attr_accessor :actions
end
- # Name of key in params that references the primary key. Will almost always be :id (default)
+ # Name of key in params that references the primary key.
+ # Will almost always be :id (default), unless you are using a plugin or have patched rails.
attr_accessor :identifier
# Name of the ActiveRecord class this resource is responsible for. Automatically determined from
- # test class if not explicitly set.
+ # test class if not explicitly set. UserTest => :user
attr_accessor :klass
# Name of the instantiated ActiveRecord object that should be used by some of the tests.
@@ -47,33 +152,62 @@ module ThoughtBot # :nodoc:
attr_accessor :object
# Name of the parent AR objects.
+ #
+ # Example:
+ # # in the routes...
+ # map.resources :companies do
+ # map.resources :people do
+ # map.resources :limbs
+ # end
+ # end
+ #
+ # # in the tests...
+ # class PeopleControllerTest < Test::Unit::TestCase
+ # should_be_restful do |resource|
+ # resource.parent = :companies
+ # end
+ # end
+ #
+ # class LimbsControllerTest < Test::Unit::TestCase
+ # should_be_restful do |resource|
+ # resource.parents = [:companies, :people]
+ # end
+ # end
attr_accessor :parent
alias parents parent
alias parents= parent=
- # Actions that should be tested. Must be a subset of #VALID_ACTIONS
+ # Actions that should be tested. Must be a subset of VALID_ACTIONS (default).
+ # Tests for each actionw will only be generated if the action is listed here.
+ #
+ # Example (for a read-only controller):
+ # resource.actions = [:show, :index]
attr_accessor :actions
- # Formats that should be tested. Must be a subset of #VALID_FORMATS
+ # Formats that should be tested. Must be a subset of VALID_FORMATS (default).
+ # Each action will be tested against the formats listed here.
+ #
+ # Example:
+ # resource.actions = [:html, :xml]
attr_accessor :formats
- # ActionOptions object
+ # ActionOptions object specifying options for the create action.
attr_accessor :create
- # ActionOptions object
+ # ActionOptions object specifying options for the update action.
attr_accessor :update
- # ActionOptions object
+ # ActionOptions object specifying options for the desrtoy action.
attr_accessor :destroy
- # ActionOptions object
+ # DeniedOptions object specifying which actions should return deny a request, and what should happen in that case.
attr_accessor :denied
def initialize # :nodoc:
@create = ActionOptions.new
@update = ActionOptions.new
@destroy = ActionOptions.new
- @denied = ActionOptions.new
+ @denied = DeniedOptions.new
@actions = VALID_ACTIONS
@formats = VALID_FORMATS
@denied.actions = []
@@ -119,7 +253,31 @@ module ThoughtBot # :nodoc:
end
end
- # Bunch of documentation and examples for this one.
+ # :section: should_be_restful
+ # Generates a full suite of tests for a restful controller.
+ #
+ # The following definition will generate tests for the +index+, +show+, +new+,
+ # +edit+, +create+, +update+ and +destroy+ actions, in both +html+ and +xml+ formats:
+ #
+ # should_be_restful do |resource|
+ # resource.parent = :user
+ #
+ # resource.create.params = { :title => "first post", :body => 'blah blah blah'}
+ # resource.update.params = { :title => "changed" }
+ # end
+ #
+ # This generates about 40 tests, all of the format:
+ # "on GET to :show should assign @user."
+ # "on GET to :show should not set the flash."
+ # "on GET to :show should render 'show' template."
+ # "on GET to :show should respond with success."
+ # "on GET to :show as xml should assign @user."
+ # "on GET to :show as xml should have ContentType set to 'application/xml'."
+ # "on GET to :show as xml should respond with success."
+ # "on GET to :show as xml should return as the root element."
+ # The +resource+ parameter passed into the block is a ResourceOptions object, and
+ # is used to configure the tests for the details of your resources.
+ #
def should_be_restful(&blk) # :yields: resource
resource = ResourceOptions.new
blk.call(resource)
@@ -138,8 +296,14 @@ module ThoughtBot # :nodoc:
end
end
+ # :section: Test macros
+
# Macro that creates a test asserting that the flash contains the given value.
# val can be a String or a Regex
+ #
+ # Example:
+ #
+ # should_set_the_flash_to /created/i
def should_set_the_flash_to(val)
should "have #{val.inspect} in the flash" do
assert_contains flash.values, val, ", Flash: #{flash.inspect}"
@@ -154,6 +318,10 @@ module ThoughtBot # :nodoc:
end
# Macro that creates a test asserting that the controller assigned to @name
+ #
+ # Example:
+ #
+ # should_assign_to :user
def should_assign_to(name)
should "assign @#{name}" do
assert assigns(name.to_sym), "The show action isn't assigning to @#{name}"
@@ -161,6 +329,10 @@ module ThoughtBot # :nodoc:
end
# Macro that creates a test asserting that the controller did not assign to @name
+ #
+ # Example:
+ #
+ # should_not_assign_to :user
def should_not_assign_to(name)
should "not assign to @#{name}" do
assert !assigns(name.to_sym), "@#{name} was visible"
@@ -187,8 +359,12 @@ module ThoughtBot # :nodoc:
end
end
+ # Macro that creates a test asserting that the controller returned a redirect to the given path.
+ # Example:
+ #
+ # should_redirect_to "/"
def should_redirect_to(url)
- should "redirect to #{url}" do
+ should "redirect to \"#{url}\"" do
instantiate_variables_from_assigns do
assert_redirected_to eval(url, self.send(:binding), __FILE__, __LINE__)
end
@@ -196,9 +372,9 @@ module ThoughtBot # :nodoc:
end
end
- module InstanceMethods
-
- private
+ module InstanceMethods # :nodoc:
+
+ private # :enddoc:
SPECIAL_INSTANCE_VARIABLES = %w{
_cookies
@@ -248,11 +424,8 @@ module ThoughtBot # :nodoc:
def make_parent_params(resource, record = nil, parent_names = nil) # :nodoc:
parent_names ||= resource.parents.reverse
-
return {} if parent_names == [] # Base case
-
parent_name = parent_names.shift
-
parent = record ? record.send(parent_name) : parent_name.to_s.classify.constantize.find(:first)
{ :"#{parent_name}_id" => parent.id }.merge(make_parent_params(resource, parent, parent_names))
diff --git a/lib/shoulda/controller_tests/formats/xml.rb b/lib/shoulda/controller_tests/formats/xml.rb
index 2fcc9293..6e10e2a7 100644
--- a/lib/shoulda/controller_tests/formats/xml.rb
+++ b/lib/shoulda/controller_tests/formats/xml.rb
@@ -1,8 +1,8 @@
module ThoughtBot # :nodoc:
module Shoulda # :nodoc:
module Controller # :nodoc:
- module XML # :nodoc:
- def self.included(other)
+ module XML
+ def self.included(other) #:nodoc:
other.class_eval do
extend ThoughtBot::Shoulda::Controller::XML::ClassMethods
end
@@ -27,7 +27,7 @@ module ThoughtBot # :nodoc:
protected
- def make_show_xml_tests(res)
+ def make_show_xml_tests(res) # :nodoc:
context "on GET to :show as xml" do
setup do
request_xml
@@ -47,15 +47,15 @@ module ThoughtBot # :nodoc:
end
end
- def make_edit_xml_tests(res)
+ def make_edit_xml_tests(res) # :nodoc:
# XML doesn't need an :edit action
end
- def make_new_xml_tests(res)
+ def make_new_xml_tests(res) # :nodoc:
# XML doesn't need a :new action
end
- def make_index_xml_tests(res)
+ def make_index_xml_tests(res) # :nodoc:
context "on GET to :index as xml" do
setup do
request_xml
@@ -74,7 +74,7 @@ module ThoughtBot # :nodoc:
end
end
- def make_destroy_xml_tests(res)
+ def make_destroy_xml_tests(res) # :nodoc:
context "on DELETE to :destroy as xml" do
setup do
request_xml
@@ -97,7 +97,7 @@ module ThoughtBot # :nodoc:
end
end
- def make_create_xml_tests(res)
+ def make_create_xml_tests(res) # :nodoc:
context "on POST to :create as xml" do
setup do
request_xml
@@ -123,7 +123,7 @@ module ThoughtBot # :nodoc:
end
end
- def make_update_xml_tests(res)
+ def make_update_xml_tests(res) # :nodoc:
context "on PUT to :update as xml" do
setup do
request_xml
diff --git a/lib/shoulda/general.rb b/lib/shoulda/general.rb
index 284a7974..6131bdca 100644
--- a/lib/shoulda/general.rb
+++ b/lib/shoulda/general.rb
@@ -1,10 +1,10 @@
module ThoughtBot # :nodoc:
module Shoulda # :nodoc:
- module General # :nodoc:
- def self.included(other)
+ module General
+ def self.included(other) # :nodoc:
other.class_eval do
extend ThoughtBot::Shoulda::General::ClassMethods
- include ThoughtBot::Shoulda::General::InstanceMethods
+ # include ThoughtBot::Shoulda::General::InstanceMethods
end
end
@@ -18,69 +18,67 @@ module ThoughtBot # :nodoc:
end
end
- module InstanceMethods
- # Prints a message to stdout, tagged with the name of the calling method.
- def report!(msg = "")
- puts("#{caller.first}: #{msg}")
+ # Prints a message to stdout, tagged with the name of the calling method.
+ def report!(msg = "")
+ puts("#{caller.first}: #{msg}")
+ end
+
+ # Ensures that the number of items in the collection changes
+ #
+ # assert_difference(User, :count, 1) { User.create }
+ # assert_difference(User.packages, :size, 3, true) { User.add_three_packages }
+ #
+ # Setting reload to true will call object.reload after the block (for ActiveRecord associations)
+ def assert_difference(object, method, difference, reload = false, msg = nil)
+ initial_value = object.send(method)
+ yield
+ object.send(:reload) if reload
+ assert_equal initial_value + difference, object.send(method), (msg || "#{object}##{method} after block")
+ end
+
+ # Ensures that object.method does not change. See assert_difference for usage.
+ def assert_no_difference(object, method, reload = false, msg = nil, &block)
+ assert_difference(object, method, 0, reload, msg, &block)
+ end
+
+ # Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
+ #
+ # assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
+ def assert_same_elements(a1, a2, msg = nil)
+ [:select, :inject, :size].each do |m|
+ [a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
end
- # Ensures that the number of items in the collection changes
- #
- # assert_difference(User, :count, 1) { User.create }
- # assert_difference(User.packages, :size, 3, true) { User.add_three_packages }
- #
- # Setting reload to true will call object.reload after the block (for ActiveRecord associations)
- def assert_difference(object, method, difference, reload = false, msg = nil)
- initial_value = object.send(method)
- yield
- object.send(:reload) if reload
- assert_equal initial_value + difference, object.send(method), (msg || "#{object}##{method} after block")
- end
+ assert a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
+ assert a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
- # Ensures that object.method does not change. See assert_difference for usage.
- def assert_no_difference(object, method, reload = false, msg = nil, &block)
- assert_difference(object, method, 0, reload, msg, &block)
- end
+ assert_equal(a1h, a2h, msg)
+ end
- # Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
- #
- # assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
- def assert_same_elements(a1, a2, msg = nil)
- [:select, :inject, :size].each do |m|
- [a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
- end
+ # Asserts that the given collection contains item x. If x is a regular expression, ensure that
+ # at least one element from the collection matches x. +extra_msg+ is appended to the error message if the assertion fails.
+ #
+ # assert_contains(['a', '1'], /\d/) => passes
+ # assert_contains(['a', '1'], 'a') => passes
+ # assert_contains(['a', '1'], /not there/) => fails
+ def assert_contains(collection, x, extra_msg = "")
+ collection = [collection] unless collection.is_a?(Array)
+ msg = "#{x.inspect} not found in #{collection.to_a.inspect} " + extra_msg
+ case x
+ when Regexp: assert(collection.detect { |e| e =~ x }, msg)
+ else assert(collection.include?(x), msg)
+ end
+ end
- assert a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
- assert a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
-
- assert_equal(a1h, a2h, msg)
- end
-
- # Asserts that the given collection contains item x. If x is a regular expression, ensure that
- # at least one element from the collection matches x. +extra_msg+ is appended to the error message if the assertion fails.
- #
- # assert_contains(['a', '1'], /\d/) => passes
- # assert_contains(['a', '1'], 'a') => passes
- # assert_contains(['a', '1'], /not there/) => fails
- def assert_contains(collection, x, extra_msg = "")
- collection = [collection] unless collection.is_a?(Array)
- msg = "#{x.inspect} not found in #{collection.to_a.inspect} " + extra_msg
- case x
- when Regexp: assert(collection.detect { |e| e =~ x }, msg)
- else assert(collection.include?(x), msg)
- end
- end
-
- # Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
- # none of the elements from the collection match x.
- def assert_does_not_contain(collection, x, extra_msg = "")
- collection = [collection] unless collection.is_a?(Array)
- msg = "#{x.inspect} found in #{collection.to_a.inspect} " + extra_msg
- case x
- when Regexp: assert(!collection.detect { |e| e =~ x }, msg)
- else assert(!collection.include?(x), msg)
- end
- end
+ # Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
+ # none of the elements from the collection match x.
+ def assert_does_not_contain(collection, x, extra_msg = "")
+ collection = [collection] unless collection.is_a?(Array)
+ msg = "#{x.inspect} found in #{collection.to_a.inspect} " + extra_msg
+ case x
+ when Regexp: assert(!collection.detect { |e| e =~ x }, msg)
+ else assert(!collection.include?(x), msg)
+ end
end
end
end