Here is what I have:
context "Create ingredient from string" do
context "1 cups butter" do
setup do
@ingredient = Ingredient.create(:ingredient_string => "1 cups butter")
end
should "return unit" do
assert_equal @ingredient.unit, 'cups'
end
should "return amount" do
assert_equal @ingredient.amount, 1.0
end
should "return name" do
assert_equal @ingredient.name, 'butter'
end
end
context "1 (18.25 ounce) package devil's food cake mix with pudding" do
setup do
@ingredient = Ingredient.create(:ingredient_string => "1 (18.25 ounce) package devil's food cake mix with pudding")
end
should "return unit" do
assert_equal @ingredient.unit, '(18.25 ounce) package'
end
should "return amount" do
assert_equal @ingredient.amount, 1.0
end
should "return name" do
assert_equal @ingredient.name, 'devil\'s food cake mix with pudding'
end
end
end
Clearly there is a lot of duplication there. Any thoughts on how to remove it, if only at the very least the context and the string?
Here's a solution to your specific problem. The idea is to create a class method (like Shoulda's context, setup and should).
Encapsulate the repetition in a class method accepting all varying parts as arguments like this:
def self.should_get_unit_amount_and_name_from_string(unit, amount, name, string_to_analyze)
context string_to_analyze do
setup do
@ingredient = Ingredient.create(:ingredient_string => string_to_analyze)
end
should "return unit" do
assert_equal @ingredient.unit, unit
end
should "return amount" do
assert_equal @ingredient.amount, amount
end
should "return name" do
assert_equal @ingredient.name, name
end
end
end
Now you can call all these encapsulated tests with one liners (5-liners here for readability ;-)
context "Create ingredient from string" do
should_get_unit_amount_and_name_from_string(
'cups',
1.0,
'butter',
"1 cups butter")
should_get_unit_amount_and_name_from_string(
'(18.25 ounce) package',
1.0,
'devil\'s food cake mix with pudding',
"1 (18.25 ounce) package devil's food cake mix with pudding")
end
In some cases, you may want to accept a block which could serve as your Shoulda setup.
Duplication in tests is not necessarily a Bad Thing(tm)
I suggest you read the following articles from Jay Field
http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html
http://blog.jayfields.com/2008/05/testing-duplicate-code-in-your-tests.html
They make a convinving case for code duplication in the tests and keeping one assertion per test.
Tests/specs are not production code and so being dry is not a priority.
The principle is that the specs should be clear to read, even if it means there is duplication of text across tests.
Don't be too concerned about specs being dry. Overemphasis of dry tests tends to make things more difficult as you have to jump around to the definitions of things to understand what is happening.
Personally for this test, I wouldn't use Shoulda. You can easily remove duplication by using dynamic method creation as follows:
class DefineMethodTest < Test::Unit::TestCase
[{:string => '1 cups butter', :unit => 'cups', :amount => 1.0, :name => 'butter'},{:string => '1 (18.25 ounce) package devil's food cake mix with pudding', :unit => '(18.25 ounce) package', :unit => 1.0, :name => "devil's food cake mix with pudding"}].each do |t|
define_method "test_create_ingredient_from_string_#{t[:string].downcase.gsub(/[^a-z0-9]+/, '_')}" do
@ingredient = Ingredient.create(:ingredient_string => t[:string])
assert_equal @ingredient.unit, t[:unit], "Should return unit #{t[:unit]}"
assert_equal @ingredient.amount, t[:amount], "Should return amount #{t[:amount]}"
assert_equal @ingredient.name, t[:name], "Should return name #{t[:name]}"
end
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.