diff --git a/lib/shoulda/general.rb b/lib/shoulda/general.rb
index ef44ac6d..66e9f3f6 100644
--- a/lib/shoulda/general.rb
+++ b/lib/shoulda/general.rb
@@ -182,9 +182,20 @@ module ThoughtBot # :nodoc:
# value by making sure the error_message_to_avoid is not
# contained within the list of errors for that attribute.
#
- # assert_good_value(User.new, :email, "user@example.com") #=> passes
- # assert_good_value(User.new, :ssn, "123456789", /length/) #=> passes
- def assert_good_value(object, attribute, value, error_message_to_avoid = //)
+ # assert_good_value(User.new, :email, "user@example.com")
+ # assert_good_value(User.new, :ssn, "123456789", /length/)
+ #
+ # If a class is passed as the first argument, a new object will be
+ # instantiated before the assertion. If an instance variable exists with
+ # the same name as the class (underscored), that object will be used
+ # instead.
+ #
+ # assert_good_value(User, :email, "user@example.com")
+ #
+ # @product = Product.new(:tangible => false)
+ # assert_good_value(Product, :price, "0")
+ def assert_good_value(object_or_klass, attribute, value, error_message_to_avoid = //)
+ object = get_instance_of(object_or_klass)
object.send("#{attribute}=", value)
object.valid?
assert_does_not_contain(object.errors.on(attribute), error_message_to_avoid, "when set to #{value.inspect}")
@@ -194,9 +205,20 @@ module ThoughtBot # :nodoc:
# value by making sure the error_message_to_expect is
# contained within the list of errors for that attribute.
#
- # assert_bad_value(User.new, :email, "invalid") #=> passes
- # assert_bad_value(User.new, :ssn, "123", /length/) #=> passes
- def assert_bad_value(object, attribute, value, error_message_to_expect = /invalid/)
+ # assert_bad_value(User.new, :email, "invalid")
+ # assert_bad_value(User.new, :ssn, "123", /length/)
+ #
+ # If a class is passed as the first argument, a new object will be
+ # instantiated before the assertion. If an instance variable exists with
+ # the same name as the class (underscored), that object will be used
+ # instead.
+ #
+ # assert_bad_value(User, :email, "invalid")
+ #
+ # @product = Product.new(:tangible => true)
+ # assert_bad_value(Product, :price, "0")
+ def assert_bad_value(object_or_klass, attribute, value, error_message_to_expect = /invalid/)
+ object = get_instance_of(object_or_klass)
object.send("#{attribute}=", value)
assert !object.valid?, "#{object.class} allowed #{value.inspect} as a value for #{attribute}"
assert object.errors.on(attribute), "There are no errors on #{attribute} after being set to #{value.inspect}"
@@ -206,6 +228,17 @@ module ThoughtBot # :nodoc:
def pretty_error_messages(obj)
obj.errors.map { |a, m| "#{a} #{m} (#{obj.send(a).inspect})" }
end
+
+ private
+
+ def get_instance_of(object_or_klass)
+ if object_or_klass.is_a?(Class)
+ klass = object_or_klass
+ instance_variable_get("@#{klass.to_s.underscore}") || klass.new
+ else
+ object_or_klass
+ end
+ end
end
end
end
diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml
new file mode 100644
index 00000000..e69de29b
diff --git a/test/other/helpers_test.rb b/test/other/helpers_test.rb
index 0478233f..e3dd098e 100644
--- a/test/other/helpers_test.rb
+++ b/test/other/helpers_test.rb
@@ -129,6 +129,20 @@ class HelpersTest < Test::Unit::TestCase # :nodoc:
assert_good_value User.new, :ssn, "x", /length/
end
end
+
+ should "accept a class as the first argument" do
+ assert_good_value User, :email, "good@example.com"
+ end
+
+ context "with an instance variable" do
+ setup do
+ @product = Product.new(:tangible => true)
+ end
+
+ should "use that instance variable" do
+ assert_good_value Product, :price, "9999", /included/
+ end
+ end
end
context "assert_bad_value" do
@@ -151,5 +165,19 @@ class HelpersTest < Test::Unit::TestCase # :nodoc:
assert_bad_value User.new, :ssn, "xxxxxxxxx", /length/
end
end
+
+ should "accept a class as the first argument" do
+ assert_bad_value User, :email, "bad"
+ end
+
+ context "with an instance variable" do
+ setup do
+ @product = Product.new(:tangible => true)
+ end
+
+ should "use that instance variable" do
+ assert_bad_value Product, :price, "0", /included/
+ end
+ end
end
end
diff --git a/test/rails_root/app/models/product.rb b/test/rails_root/app/models/product.rb
new file mode 100644
index 00000000..1108a44d
--- /dev/null
+++ b/test/rails_root/app/models/product.rb
@@ -0,0 +1,9 @@
+class Product < ActiveRecord::Base
+ validates_presence_of :title, :price
+ validates_inclusion_of :price, :in => 0..99, :unless => :tangible
+
+ validates_inclusion_of :price, :in => 1..9999, :if => :tangible
+ validates_inclusion_of :weight, :in => 1..100, :if => :tangible
+ validates_format_of :size, :with => /.+x.+x.+/, :if => :tangible
+ validates_length_of :size, :in => 5..20, :if => :tangible
+end
diff --git a/test/rails_root/db/migrate/009_create_products.rb b/test/rails_root/db/migrate/009_create_products.rb
new file mode 100644
index 00000000..173f948f
--- /dev/null
+++ b/test/rails_root/db/migrate/009_create_products.rb
@@ -0,0 +1,17 @@
+class CreateProducts < ActiveRecord::Migration
+ def self.up
+ create_table :products do |t|
+ t.string :title
+ t.integer :price
+ t.integer :weight
+ t.string :size
+ t.boolean :tangible
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :products
+ end
+end