Agile Web Development with Rails, Edition 4

Agile Web Development with Rails, Edition 4

7.3 Playtime 7.1 Iteration B1: Validate!

7.2 Iteration B2: Unit Testing

Introduce the importance of unit testing.

Look at what files are generated

ls test/unit
helpers
product_test.rb

Add a fixture.

edit test/fixtures/products.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
 
one:
  title: MyString
  description: MyText
  image_url: MyString
  price: 9.99
 
two:
  title: MyString
  description: MyText
  image_url: MyString
  price: 9.99
 
ruby: 
  title:       Programming Ruby 1.9
  description: 
    Ruby is the fastest growing and most exciting dynamic
    language out there.  If you need to get working programs
    delivered fast, you should add Ruby to your toolbox.
  price:       49.50
  image_url:   ruby.png 

Now run the tests... and watch them fail :-(

rake test
(in /home/rubys/svn/rails4/Book/util/work-193/depot)
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.3-r28190%global/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
.
Finished in 0.053996 seconds.
 
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
 
Test run options: --seed 2490
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.3-r28190%global/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
F.....F
Finished in 0.303124 seconds.
 
  1) Failure:
test_should_create_product(ProductsControllerTest) [test/functional/products_controller_test.rb:20]:
"Product.count" didn't change by 1.
<4> expected but was
<3>.
 
  2) Failure:
test_should_update_product(ProductsControllerTest) [test/functional/products_controller_test.rb:39]:
Expected response to be a <:redirect>, but was <200>.
Expected block to return true value.
 
7 tests, 9 assertions, 2 failures, 0 errors, 0 skips
 
Test run options: --seed 21440
Errors running test:functionals!

Solution is simple, provide valid data.

edit test/functional/products_controller_test.rb
require 'test_helper'
 
class ProductsControllerTest < ActionController::TestCase
  # ...
  setup do
    @product = products(:one)
    @update = {
      :title       => 'Lorem Ipsum',
      :description => 'Wibbles are fun!',
      :image_url   => 'lorem.jpg',
      :price       => 19.95
    }
  end
 
  test "should get index" do
    get :index
    assert_response :success
    assert_not_nil assigns(:products)
  end
 
  test "should get new" do
    get :new
    assert_response :success
  end
 
  test "should create product" do
    assert_difference('Product.count') do
      post :create, :product => @update
    end
 
    assert_redirected_to product_path(assigns(:product))
  end
  test "should update product" do
    put :update, :id => @product.to_param, :product => @update
    assert_redirected_to product_path(assigns(:product))
  end
end

Tests now pass again :-)

rake test
(in /home/rubys/svn/rails4/Book/util/work-193/depot)
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.3-r28190%global/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
.
Finished in 0.053070 seconds.
 
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
 
Test run options: --seed 56788
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.3-r28190%global/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
.......
Finished in 0.258406 seconds.
 
7 tests, 10 assertions, 0 failures, 0 errors, 0 skips
 
Test run options: --seed 54616

Add some unit tests for new function.

edit test/unit/product_test.rb
require 'test_helper'
 
class ProductTest < ActiveSupport::TestCase
  test "product attributes must not be empty" do
    product = Product.new
    assert product.invalid?
    assert product.errors[:title].any?
    assert product.errors[:description].any?
    assert product.errors[:price].any?
    assert product.errors[:image_url].any?
  end
 
  test "product price must be positive" do
    product = Product.new(:title       => "My Book Title",
                          :description => "yyy",
                          :image_url   => "zzz.jpg")
    product.price = -1
    assert product.invalid?
    assert_equal "must be greater than or equal to 0.01", 
      product.errors[:price].join('; ')
 
    product.price = 0
    assert product.invalid?
    assert_equal "must be greater than or equal to 0.01", 
      product.errors[:price].join('; ')
 
    product.price = 1
    assert product.valid?
  end
 
  def new_product(image_url)
    Product.new(:title       => "My Book Title",
                :description => "yyy",
                :price       => 1,
                :image_url   => image_url)
  end
 
  test "image url" do
    ok = %w{ fred.gif fred.jpg fred.png FRED.JPG FRED.Jpg
             http://a.b.c/x/y/z/fred.gif }
    bad = %w{ fred.doc fred.gif/more fred.gif.more }
    
    ok.each do |name|
      assert new_product(name).valid?, "#{name} shouldn't be invalid"
    end
 
    bad.each do |name|
      assert new_product(name).invalid?, "#{name} shouldn't be valid"
    end
  end
 
  test "product is not valid without a unique title" do
    product = Product.new(:title       => products(:ruby).title,
                          :description => "yyy", 
                          :price       => 1, 
                          :image_url   => "fred.gif")
 
    assert !product.save
    assert_equal "has already been taken", product.errors[:title].join('; ')
  end
 
  test "product is not valid without a unique title - i18n" do
    product = Product.new(:title       => products(:ruby).title,
                          :description => "yyy", 
                          :price       => 1, 
                          :image_url   => "fred.gif")
 
    assert !product.save
    assert_equal I18n.translate('activerecord.errors.messages.taken'),
                 product.errors[:title].join('; ')
  end
  
end

Tests pass!

rake test:units
(in /home/rubys/svn/rails4/Book/util/work-193/depot)
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.3-r28190%global/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
.....
Finished in 0.181190 seconds.
 
5 tests, 23 assertions, 0 failures, 0 errors, 0 skips
 
Test run options: --seed 65407

7.3 Playtime 7.1 Iteration B1: Validate!