bad --> @line_item = @cart.add_product(:product.id)
good --> @line_item = @cart.add_product(product.id)
First case not passing id, instead passing (I think) an object reference! gotta love those one character typos!
Saturday, January 15, 2011
Rails - Iteration E1 - line_item aggregation starts new
Take 2 on smarter cart!
First: add a unit test for the cart.add_product method
test "cart aggregates line items" do
c = carts(:one)
assert_equal 0, c.line_items.count, "cart items not empty at start of test"
p = products(:ruby)
item = c.add_product(p.id)
assert_equal 1, c.line_items.count, "first product add did not create new line item"
assert_equal 1, item.quantity, "new item quantity not 1"
item = c.add_product(p.id) #hold reference for future testing
assert_equal 1, c.line_items.count, "readd product caused increase in line items count"
assert_equal products(:ruby).id, item.product.id, "a problem with product id between line item and orig"
end
(Note to self: originally tried to do above like:
- assert_difference('c.line_items.count') do
- p = products(:ruby)
- c.add_product(p.id)
- c.save
- end
Note that the assert_difference requires a DB save event. Decided I didn't like that extra dependency on a unit test. Also note that the assert_difference requires a string not an object expression.)
Second: modify line_item_controller test to show aggregation of line items
test "should create/update line_item" do
#original test when just creating new items (iteration D)
assert_difference('LineItem.count') do
post :create, :product_id => products(:ruby).id
end
assert_redirected_to cart_path(assigns(:line_item).cart)
#new test - just repeat old and this time no_difference
assert_no_difference('LineItem.count') do
post :create, :product_id => products(:ruby).id
end
assert_redirected_to cart_path(assigns(:line_item).cart)
now when I complete the line item tests everything (including listing of cart after add) works as intended.
First: add a unit test for the cart.add_product method
test "cart aggregates line items" do
c = carts(:one)
assert_equal 0, c.line_items.count, "cart items not empty at start of test"
p = products(:ruby)
item = c.add_product(p.id)
assert_equal 1, c.line_items.count, "first product add did not create new line item"
assert_equal 1, item.quantity, "new item quantity not 1"
item = c.add_product(p.id) #hold reference for future testing
assert_equal 1, c.line_items.count, "readd product caused increase in line items count"
assert_equal products(:ruby).id, item.product.id, "a problem with product id between line item and orig"
end
(Note to self: originally tried to do above like:
- assert_difference('c.line_items.count') do
- p = products(:ruby)
- c.add_product(p.id)
- c.save
- end
Note that the assert_difference requires a DB save event. Decided I didn't like that extra dependency on a unit test. Also note that the assert_difference requires a string not an object expression.)
Second: modify line_item_controller test to show aggregation of line items
test "should create/update line_item" do
#original test when just creating new items (iteration D)
assert_difference('LineItem.count') do
post :create, :product_id => products(:ruby).id
end
assert_redirected_to cart_path(assigns(:line_item).cart)
#new test - just repeat old and this time no_difference
assert_no_difference('LineItem.count') do
post :create, :product_id => products(:ruby).id
end
assert_redirected_to cart_path(assigns(:line_item).cart)
now when I complete the line item tests everything (including listing of cart after add) works as intended.
Rails - Iteration E1 - recovery from new defect introduced
Interesting, at end of last class all of my tests passed, but today I test my app and find that I have a critical defect!
Post /lines_items?product_id=4 fails horribly when trying to show the cart page with updated list.
Cart page complains about no 'title' attribute for a nil object
/line_items shows that my cart now has a line item for a product id 123456 <- which of course doesn't exist.
Old instincts: well time to add a check for null products and skip processing anything won't work.
New TDD instincts: Whoa! how can all my tests be passing? Why is the product id changing on add_product?
Step 1: Recreate the defect in a test.
Looks like my line_items_controller_test only confirms line item addition and redirect to cart page. But defect manifest at cart page....
Decided to try my hand at 'integration' test, since now testing flow from one controller to another.
> rails generate integration_test user_cart_session_test
Then add test method to the resulting skeleton:
test "line item adds to cart" do
item = products(:ruby)
post "/line_items", :product_id => item.id
follow_redirect!
assert_response :success
end
> rake test:integration --> Same problem as reported in the app!
Last Step: Rollback to a clean branch of code and start lesson over.
> git commit -a -m "test for my broken code"
> git checkout master
> rake test --> lots of problems? Wait the DB was mucked with
> rake db:drop
> rake db:migrate
> rake test --> ah! that's better!
> git checkout class.chapter10 test/integration/ --> pull over my new fangled test
rails test --> look now it's passing, got rid of my bug!
git add test/integration/
git commit -m "adding a functional test for cart and line items"
Note To Self: never ever forget to branch when starting new development! Love this cycle
1) break code unexpectedly
2) build test to reproduce break
3) revert code, import test and start dev over again (and always have access to previous work!)
Post /lines_items?product_id=4 fails horribly when trying to show the cart page with updated list.
Cart page complains about no 'title' attribute for a nil object
/line_items shows that my cart now has a line item for a product id 123456 <- which of course doesn't exist.
Old instincts: well time to add a check for null products and skip processing anything won't work.
New TDD instincts: Whoa! how can all my tests be passing? Why is the product id changing on add_product?
Step 1: Recreate the defect in a test.
Looks like my line_items_controller_test only confirms line item addition and redirect to cart page. But defect manifest at cart page....
Decided to try my hand at 'integration' test, since now testing flow from one controller to another.
> rails generate integration_test user_cart_session_test
Then add test method to the resulting skeleton:
test "line item adds to cart" do
item = products(:ruby)
post "/line_items", :product_id => item.id
follow_redirect!
assert_response :success
end
> rake test:integration --> Same problem as reported in the app!
Last Step: Rollback to a clean branch of code and start lesson over.
> git commit -a -m "test for my broken code"
> git checkout master
> rake test --> lots of problems? Wait the DB was mucked with
> rake db:drop
> rake db:migrate
> rake test --> ah! that's better!
> git checkout class.chapter10 test/integration/ --> pull over my new fangled test
rails test --> look now it's passing, got rid of my bug!
git add test/integration/
git commit -m "adding a functional test for cart and line items"
Note To Self: never ever forget to branch when starting new development! Love this cycle
1) break code unexpectedly
2) build test to reproduce break
3) revert code, import test and start dev over again (and always have access to previous work!)
Wednesday, January 5, 2011
Rails4 - Iteration D3 - functional tests for new cart button
When building the store page, we went through several examples of adding functional tests to confirm the store index page contained expected page elements. Seemed logical to extend these existing tests to track the addition of the cart button. In doing so learned a couple of interesting features of assert_select
1) use a block to test that each .entry now displays button "Add to Cart"
assert_select '.entry' do
assert_select '.button_to input[value=?]', /Add to Cart/
end
2) check that each form for Add to Cart contains the product_id information, in this case using a substitution marker ? to check the action contents.
assert_select '.entry form[action=?]', /.*product_id=\d+.*/
Note: the substitution regex did not recognize partial matches. For example, /product_id=\d+/ failed to find any elements because 'product_id' is only a partial match to the actual action value /line_items?product_id=4 needed to add .* at start and end to pad out for the remaining action text.
Note for future study: How do I put variables into the regex? for example might be nice to check that the action also contains correct target 'line_items' but I don't really want to hard code that. Instead want to test with line_items_path variable?
1) use a block to test that each .entry now displays button "Add to Cart"
assert_select '.entry' do
assert_select '.button_to input[value=?]', /Add to Cart/
end
2) check that each form for Add to Cart contains the product_id information, in this case using a substitution marker ? to check the action contents.
assert_select '.entry form[action=?]', /.*product_id=\d+.*/
Note: the substitution regex did not recognize partial matches. For example, /product_id=\d+/ failed to find any elements because 'product_id' is only a partial match to the actual action value /line_items?product_id=4 needed to add .* at start and end to pad out for the remaining action text.
Note for future study: How do I put variables into the regex? for example might be nice to check that the action also contains correct target 'line_items' but I don't really want to hard code that. Instead want to test with line_items_path variable?
Friday, December 31, 2010
Rails4 - Iteration B2 Product controller tests fail.
The book solution demos adding an @update product not conflicting with the default fixtures :one and :two, which by default have the same title. (after adding uniqueness test on title, these fail).
It also works to fix the test data, just update the products.yml file and provide a unique title for :one and :two
one:
title: MyString One
description: MyText
image_url: MyString
price: 9.99
two:
title: MyString Two
description: MyText
image_url: MyString
price: 9.99
This allows the update test to pass no problem.
Note: you do still need to provide a unique title in should create product (where using :one as a template). Simple as adding one line into the method:
#after validation added, now required to provide a unique title.
@product.title="a unique test title"
#this is the part that takes the form input..
post :create, :product => @product.attributes
It also works to fix the test data, just update the products.yml file and provide a unique title for :one and :two
one:
title: MyString One
description: MyText
image_url: MyString
price: 9.99
two:
title: MyString Two
description: MyText
image_url: MyString
price: 9.99
This allows the update test to pass no problem.
Note: you do still need to provide a unique title in should create product (where using :one as a template). Simple as adding one line into the method:
#after validation added, now required to provide a unique title.
@product.title="a unique test title"
#this is the part that takes the form input..
post :create, :product => @product.attributes
Thursday, December 30, 2010
Rails4: Iteration B2 - generic def for new default valid products
Building on example for "def new_product(image_url)" pg. 101
#a generic method to build new test produce with valid data
#but also allow insertion of any attributes that need to be over-ridden
#takes a hash of attribute values, checks for each and populates default as needed.
#don't add this to real Product, 'cause production code shouldn't provide defaults.
def new_valid_product(data)
Product.new(
:title => data.has_key?(:title) ? data[:title] : "A Book Title",
:description => data.has_key?(:description) ? data[:description] : "ipso lorum... a default description",
:price => data.has_key?(:price) ? data[:price] : 123.45,
:image_url => data.has_key?(:image_url) ? data[:image_url] : "test.gif"
)
end
#a quick test to warn me if any rules change that invalidate the default values.
test "default product attributes are valid" do
product = new_valid_product({})
assert !product.errors[:title].any?, "default title in error"
assert !product.errors[:description].any?, "default_description in error"
assert !product.errors[:price].any?, "default_price in error"
assert !product.errors[:image_url].any?, "default image_url in error"
end
#a quick test to prove to myself that the method accepts input data & uses it correctly.
test "default product attributes can be changed" do
product = new_valid_product({:title => "T", :description => "D", :price => 1, :image_url => "I"})
assert_equal "T", product.title
assert_equal "D", product.description
assert_equal 1, product.price
assert_equal "I", product.image_url
end
Note: My guess it that this kind of method is not really useful, Ruby has fixtures, my suspicion is that in time I'll find it much easier to define and get a fixture, then change a value or two, rather than write this kind of utility method for each model unit test class.
#a generic method to build new test produce with valid data
#but also allow insertion of any attributes that need to be over-ridden
#takes a hash of attribute values, checks for each and populates default as needed.
#don't add this to real Product, 'cause production code shouldn't provide defaults.
def new_valid_product(data)
Product.new(
:title => data.has_key?(:title) ? data[:title] : "A Book Title",
:description => data.has_key?(:description) ? data[:description] : "ipso lorum... a default description",
:price => data.has_key?(:price) ? data[:price] : 123.45,
:image_url => data.has_key?(:image_url) ? data[:image_url] : "test.gif"
)
end
#a quick test to warn me if any rules change that invalidate the default values.
test "default product attributes are valid" do
product = new_valid_product({})
assert !product.errors[:title].any?, "default title in error"
assert !product.errors[:description].any?, "default_description in error"
assert !product.errors[:price].any?, "default_price in error"
assert !product.errors[:image_url].any?, "default image_url in error"
end
#a quick test to prove to myself that the method accepts input data & uses it correctly.
test "default product attributes can be changed" do
product = new_valid_product({:title => "T", :description => "D", :price => 1, :image_url => "I"})
assert_equal "T", product.title
assert_equal "D", product.description
assert_equal 1, product.price
assert_equal "I", product.image_url
end
Note: My guess it that this kind of method is not really useful, Ruby has fixtures, my suspicion is that in time I'll find it much easier to define and get a fixture, then change a value or two, rather than write this kind of utility method for each model unit test class.
Tuesday, December 28, 2010
Rails4 - curl to download directory content
thanks to: http://www.linuxquestions.org/questions/programming-9/using-curl-to-dl-files-from-http-sites-with-wildcard-379067/ for the initial curl script line and to http://www.linuxquestions.org/questions/programming-9/extract-substring-using-sed-and-regular-expressions-regexp-702074/ for help extracting the filename from html.
for file in `curl http://media.pragprog.com/titles/rails4/code/depot_b/public/images/ | perl -wlne 'print $1 if
/href="(.*[gif|png|jpg])">/'`;do curl -o ${file} http://media.pragprog.com/titles/rails4/code/depot_b/public/images/${file};done
*Note: not happy with having to specify the file types, but the page HTML contains other links like Download, Parent etc so href=.* litters the results with junk files.
*Note would have been easier with wget, but for some reason darwin-ports for leopard DMG was unavailable.
should I get darwin-ports installed try this instead.
run from depot/public/images directory
wget -rkp -np -nH --cut-dirs=1 http://media.pragprog.com/titles/rails4/code/depot_b/public/images
@see http://psung.blogspot.com/2008/06/using-wget-or-curl-to-download-web.html
for file in `curl http://media.pragprog.com/titles/rails4/code/depot_b/public/images/ | perl -wlne 'print $1 if
/href="(.*[gif|png|jpg])">/'`;do curl -o ${file} http://media.pragprog.com/titles/rails4/code/depot_b/public/images/${file};done
*Note: not happy with having to specify the file types, but the page HTML contains other links like Download, Parent etc so href=.* litters the results with junk files.
*Note would have been easier with wget, but for some reason darwin-ports for leopard DMG was unavailable.
should I get darwin-ports installed try this instead.
run from depot/public/images directory
wget -rkp -np -nH --cut-dirs=1 http://media.pragprog.com/titles/rails4/code/depot_b/public/images
@see http://psung.blogspot.com/2008/06/using-wget-or-curl-to-download-web.html
Subscribe to:
Posts (Atom)