Escolar Documentos
Profissional Documentos
Cultura Documentos
1
What Kind of
Coverage are You?
2
lining up
3
module Math
def max(a, b)
if a > b then a else b end
end
end
require "test/unit"
require "math"
def test_max
assert_equal 2, max(2, 1)
end
end
4
5
module Math
def max(a, b)
if a > b then
a
else
b
end
end
end
6
7
require "test/unit"
require "math"
def test_first_parameter_as_max
assert_equal 2, max(2, 1)
end
def test_second_parameter_as_max
assert_equal 2, max(1, 2)
end
end
8
9
branching out
10
// Math.java
public class Math {
public static int max(int a, int b) {
return a > b ? a : b;
}
}
// MathTest.java
import junit.framework.TestCase;
11
12
import junit.framework.TestCase;
13
14
101 coverage measures
kaner.com/coverage.htm
15
8 Flavors of Fail
16
Underspecification
17
Underspecification
http://www.flickr.com/photos/abuzavi/92491999
18
“While it's possible to specify a
product ambiguously, it is not
possible to build a product
ambiguously.”
Tom DeMarco and Timothy Lister
19
# code under test
#############################
MIN_FREE_SHIPPING_PRICE = 25.0
def free_shipping?(total_order_price)
total_order_price > MIN_FREE_SHIPPING_PRICE
end
20
# code under test
#############################
MIN_FREE_SHIPPING_PRICE = 25.0
def free_shipping?(total_order_price)
total_order_price > MIN_FREE_SHIPPING_PRICE
end
# test suite?
#############################
def test_free_shipping_returns_true_for_order_above_min_price
assert free_shipping?(MIN_FREE_SHIPPING_PRICE + 1)
end
def test_free_shipping_returns_false_for_order_below_min_price
assert !free_shipping?(MIN_FREE_SHIPPING_PRICE - 1)
end
21
what about my
$25.00 order!
22
# code under test
#############################
MIN_FREE_SHIPPING_PRICE = 25.0
def free_shipping?(total_order_price)
total_order_price >=
= MIN_FREE_SHIPPING_PRICE
end
# test suite
#############################
def test_free_shipping_returns_true_for_order_above_min_price
assert free_shipping?(MIN_FREE_SHIPPING_PRICE + 1)
end
def test_free_shipping_returns_false_for_order_below_min_price
assert !free_shipping?(MIN_FREE_SHIPPING_PRICE - 1)
end
def test_free_shipping_returns_true_for_order_equal_to_min_price
assert free_shipping?(MIN_FREE_SHIPPING_PRICE)
end
23
edge cases matter
24
“Code coverage can only tell you
how much of your code is being
tested. It cannot tell you how
much code you still need to write.”
Eric Sink
25
Incidental Coverage
26
class ProductsController < ApplicationController
def index
@products = Product.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @products }
end
end
end
27
class ProductsControllerTest < ActionController::TestCase
def test_should_get_index
get :index
end
end
28
29
failure to assert
30
testing != System.out.println()
31
class ProductsControllerTest < ActionController::TestCase
def test_should_get_index
get :index
assert_response :success
assert_not_nil assigns(:products)
end
end
32
100% covered
50% tested
33
def index
@products = Product.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @products }
end
end
34
def index
@products = Product.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { raise :fatal_error }
end
end
def test_should_get_index_formatted_for_xml
@request.env['HTTP_ACCEPT'] = 'application/xml'
get :index
assert_response :success
assert_not_nil assigns(:products)
end
36
Invisible Code
37
<% if (@article.published_at + @article.comment_age.days) > Time.now %>
<% form_for(@comment) do |f| %>
<!-- capture witty comment here (maybe) -->
<% end %>
<% end %>
38
<% if (@article.published_at + @article.comment_age.days) > Time.now %>
<% form_for(@comment) do |f| %>
<!-- capture witty comment here (maybe) -->
<% end %>
<% end %>
39
<% if @article.accept_comments? %>
<% form_for(@comment) do |f| %>
<!-- capture witty comment here -->
<% end %>
<% end %>
def comments_expired_at
published_at + comment_age.days
end
end
41
class ArticleTest < ActiveSupport::TestCase
def test_comments_always_accepted_when_comment_days_is_zero
a = Article.new(:comment_age => 0, :published_at => 5.years.ago.utc)
assert a.accept_comments?
end
def test_comments_accepted_prior_to_expiration
a = Article.new(:comment_age => 5, :published_at => 2.days.ago.utc)
assert a.accept_comments?
end
def test_comments_not_accepted_past_expiration
a = Article.new(:comment_age => 10, :published_at => 12.days.ago.utc)
assert !a.accept_comments?
end
end
42
Overspecification
43
class ProductsControllerTest < ActionController::TestCase
def test_something
product = Product.create(:name => "Frisbee", :price => 5.00)
get :show, :id => product.id
assert_response :success
product = assigns(:product)
assert_not_nil product
assert product.valid?
assert product.name == "Frisbee"
assert product.price == 5.00
end
end
44
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
end
end
45
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
end
end
46
communicate essence
47
Fragile Mocking
48
http://www.flickr.com/photos/mrbill/96827834
49
# code under test - round 1
#############################
def create
@product = Product.create(params[:product])
if !@product.new_record?
flash[:notice] = 'Product was successfully created.'
redirect_to(@product)
else
render :action => "new"
end
end
50
# fragile mocking - round 1
#############################
def test_should_create_product
product = Product.new
product.stubs(:new_record?).returns(false)
Product.expects(:create).returns(product)
post :create, :product => {}
end
51
# code under test - round 2
#############################
def create
@product = Product.new(params[:product])
if @product.save
flash[:notice] = 'Product was successfully created.'
redirect_to(@product)
else
render :action => "new"
end
end
52
# fragile mocking - round 2
#############################
def test_should_create_product
Product.any_instance.expects(:save).returns(true)
post :create, :product => {}
end
53
# fragile mocking - round 1
#############################
Fail!
def test_should_create_product
product = Product.new
product.stubs(:new_record?).returns(false)
Product.expects(:create).returns(product)
post :create, :product => {}
end
54
def test_should_create_product
assert_difference('Product.count') do
post :create, :product => {"name" => "Frisbee", "price" => 5.00}
end
end
55
"Well written tests specify as
little implementation detail as
possible."
Jay Fields
56
The Ugly Mirror
57
# code under test
#############################
User = Struct.new(:first_name, :last_name, :email) do
def to_s
"#{last_name}, #{first_name} <#{email}>"
end
end
58
# code under test
#############################
User = Struct.new(:first_name, :last_name, :email) do
def to_s
"#{last_name}, #{first_name} <#{email}>"
end
end
# test suite?
#############################
def test_to_s_includes_name_and_email
user = User.new("John", "Smith", "jsmith@example.com")
assert_equal "#{user.last_name}, #{user.first_name} <#{user.email}>",
user.to_s
end
59
# code under test
#############################
User = Struct.new(:first_name, :last_name, :email) do
def to_s
"#{last_name}, #{first_name} <#{email}>"
end
end
# test suite
#############################
def test_to_s_includes_name_and_email
user = User.new("John", "Smith", "jsmith@example.com")
assert_equal "#{user.last_name}, #{user.first_name} <#{user.email}>",
user.to_s
assert_equal "Smith, John <jsmith@example.com>", user.to_s
end
60
expect literals
61
Slow Tests
62
http://www.flickr.com/photos/cindy47452/441316645
63
Shallow Tests
64
http://www.flickr.com/photos/addictive_picasso/442553488
65
"No automated test suite can ever
replace exploratory testing."
Jay Fields
66
http://www.flickr.com/photos/jasmic/279741827
67
“[Coverage tools] are only
helpful if they’re used to enhance
thought, not replace it.”
Brian Marick
68
Thanks to Jason Rudolph, for preparing these
slides based on Relevance’s experience with
100% test coverage over the past year, and to
the whole Relevance team.
Slides: jasonrudolph.com/downloads
Blog: jasonrudolph.com/blog
Relevance: thinkrelevance.com
This presentation is published under the Creative Commons Attribution Noncommercial Share Alike License Version 3.0.
(Please see http://creativecommons.org/licenses/by-nc-sa/3.0 for complete details.)
69