- write a test first
- see it fail
- write the implementation
- see the test pass "the red green loop"
NOTE: Some say: only write essential test initially. Add edge cases later.
- started around 2003, XP (software dev methodology meant to accommodate changing customer requirements)
- popular, but not as widely adopted as it may seem
Advantages:
- better understanding of the problem
- instant feedback when a change causes an error
- eliminate bugs early (costs less)
Disadvantages:
- "test induced design damage" - DHH
- passing unit tests can give a false sense of security. need integration tests too. need to test how your classes interact with each other and with external things (libraries you use, databases, internet services)
One of three major ruby testing frameworks.
- RSpec
- MiniTest
- TestUnit
- Mocha (not a full test framework, just for mocking... we'll talk about that later)
RSpec is a ruby gem. You can find the source code online here: https://github.com/rspec/rspec
If you are working with a pure ruby app, your folder structure might look like this:
lib/
product.rb
spec/
spec_helper.rb
product_spec.rb
Code a small ruby app that keeps track of products and customer carts.
Introduce Guard
Starter code: [email protected]:lighthouse-labs/tdd_products_and_carts.git
Have a name
and a price_in_cents
.
Are used to store products. Should be able to:
- retrieve the list of products in a cart
- add a product to the cart
- check if the cart is empty
- get the total dollar amount for the products in the cart
- check if the customer is eligible for free shipping. this happens when the total cart value > $100
# spec/product_spec.rb
describe "Product" do
describe "#initialize" do
it "should create a product with a name and price"
it "should raise an error if the price was not given"
end
end
# spec/cart_spec.rb
describe "Cart" do
describe "#initialize" do
it "should create a cart" # test without assertion
it "should have no products in the cart" # test exception
end
describe "products" do
it "should return a list of products" # attr_accessors should be tested b/c they are just methods
end
describe "#add_product" do
it "should add a product to the cart" # use instance_variable_get(@products)
it "should raise an ArgumentError if product is not a Product"
# it "should raise an InvalidProductError if product is not a Product"
end
describe "#empty?" do
it "should return true if the cart is empty"
it "should return false if there is an item"
end
end
Anyone wonder why we used price_in_cents to store the product price? Why not use a data type that looks more like a decimal?
You lose precision
1.0e9 + 1.0e-9
#=> 1000000000.0
Even with smaller numbers
0.2 - 0.05
#=> 0.15000000000000002
Even if statements don't act as you would expect
1.20 - 1.00 == 0.20
#=> false