4. Test-driven development
From Wikipedia, the free encyclopedia
Test-driven development (TDD) is a software
development process that relies on the repetition
of a very short development cycle: first the
developer writes a failing automated test case that
defines a desired improvement or new function,
then produces code to pass that test and finally
refactors the new code to acceptable standards.
28. Anatomy of a test
# Given some test accounts
➊ Setup account_1 = Account.new(100)
account_2 = Account.new(50)
# When I transfer money
➋ Act transfer(20, from: account_1,
to: account_2)
# Then the balances should be updated
➌ Assert account_1.balance.should eq(80)
account_2.balance.should eq(70)
30. Acceptance tests
Feature: Logging in and out
Scenario: User is greeted on login
Given the user "Kerry" has an account
When he logs in
Then he should see "Welcome, Kerry"
31. Acceptance tests
$ cucumber login.feature
Feature: Logging in and out
Scenario: User is greeted on login
Given the user "Kerry" has an account
When he logs in
Then he should see "Welcome, Kerry"
1 scenario (1 undefined) 3 steps (3 undefined) 0m0.002s
# login.feature:3 # login.feature:4 # login.feature:5 # login.feature:6
You can implement step definitions for undefined steps with
Given /^the user "(.*?)" has an account$/ do |arg1|
pending # express the regexp above with the code you wish
end
...
32. Acceptance tests
Given /^the user "(.*?)" has an account$/ do |username|
@user = User.create username: username, password: secret
end
When /^he logs in$/ do
visit "/login"
fill_in "User name", :with => @user.username
fill_in "Password", :with => "secret"
click_button "Log in"
end
Then /^he should see "(.*?)"$/ do |text|
page.should have_text(text)
end
33. Acceptance tests
$ cucumber login.feature
Feature: Logging in and out
Scenario: User is greeted on login
Given the user "Kerry" has an account # steps.rb:24
When he logs in # steps.rb:28
Then he should see "Welcome, Kerry" # steps.rb:35
expected there to be content "Log out" in "Welcome to the site!nThere's
nothing here yet.” (RSpec::Expectations::ExpectationNotMetError)
./steps.rb:36:in `/^he should see "(.*?)"$/'
login.feature:6:in `Then he should see "Welcome, Kerry"'
Failing Scenarios:
cucumber login.feature:3 # Scenario: User is greeted on login
1 scenario (1 failed)
3 steps (1 failed, 2 passed) 0m0.002s
34. Acceptance tests
$ cucumber login.feature
Feature: Logging in and out
Scenario: User is greeted on login
Given the user "Kerry" has an account # steps.rb:24
When he logs in # steps.rb:28
Then he should see "Welcome, Kerry" # steps.rb:35
1 scenario (1 passed) 3 steps (3 passed) 0m0.003s
39. Unit tests
$ rspec calculator_spec.rb
F
Failures:
1) Calculator can add two numbers
Failure/Error: calculator.add(2, 3).should eq(5)
NoMethodError:
undefined method `add' for #<Calculator:0x007fa0ac1ea718>
# ./calculator_spec.rb:6:in `block (2 levels) in <top (required)>'
Finished in 0.00035 seconds
1 example, 1 failure
Failed examples:
rspec ./calculator_spec.rb:4 # Calculator can add two numbers
41. Unit tests
$ rspec calculator_spec.rb
F
Failures:
1) Calculator can add two numbers
Failure/Error: calculator.add(2, 3).should eq(" 5")
ArgumentError: wrong number of arguments (2 for 0)
# ./calculator.rb:2:in `add'
# ./calculator_spec.rb:6:in `block (2 levels) in <top (required)>'
Finished in 0.0005 seconds
1 example, 1 failure
Failed examples:
rspec ./calculator_spec.rb:4 # Calculator can add two numbers
45. Unit tests
$ rspec calculator_spec.rb
.
Finished in 0.00065 seconds
1 example, 0 failures
46. Unit tests
describe Calculator do
it "can add two numbers" do
calculator = Calculator.new
calculator.add(2, 3).should eq(" 5")
end
it "pads output to eight characters" do
calculator = Calculator.new
calculator.add(2, 2).should eq(" 4")
calculator.add(5, 5).should eq(" 10")
end
end
47. Unit tests
$ rspec calculator_spec.rb
.F
Failures:
1) Calculator pads output to eight characters
Failure/Error: calculator.add(5, 5).should eq(" 10")
expected: " 10"
got: " 10"
(compared using ==)
# ./calculator_spec.rb:12:in `block (2 levels) in <top (required)>'
Finished in 0.00086 seconds
2 examples, 1 failure
Failed examples:
rspec ./calculator_spec.rb:9 # Calculator pads output to eight characters
49. Unit tests
$ rspec calculator_spec.rb
..
Finished in 0.00076 seconds
2 examples, 0 failures
50. Unit tests
describe Calculator do
it "can add two numbers" do
calculator = Calculator.new
calculator.add(2, 3).should eq(" 5")
end
it "pads output to eight characters" do
calculator = Calculator.new
calculator.add(2, 2).should eq(" 4")
calculator.add(5, 5).should eq(" 10")
end
end
51. Unit tests
describe Calculator do
before do
@calculator = Calculator.new
end
it "can add two numbers" do
@calculator.add(2, 3).should eq(" 5")
end
it "pads output to eight characters" do
@calculator.add(2, 2).should eq(" 4")
@calculator.add(5, 5).should eq(" 10")
end
end
52. Unit tests
$ rspec calculator_spec.rb
..
Finished in 0.00075 seconds
2 examples, 0 failures
53. Unit tests
describe Calculator do
before do
@calculator = Calculator.new
end
it "can add two numbers" do
@calculator.add(2, 3).should eq(" 5")
end
it "can subtract two numbers" do
@calculator.subtract(3, 1).should eq(" 2")
end
it "pads output to eight characters" do
@calculator.add(2, 2).should eq(" 4")
@calculator.add(5, 5).should eq(" 10")
end
end
57. Unit tests
class Calculator
def add a, b
format(a + b)
end
def subtract a, b
format(a - b)
end
private
def format number
"%8i" % number
end
end
58. Unit tests
$ rspec calculator_spec.rb
...
Finished in 0.00086 seconds
3 examples, 0 failures
59. Unit tests
describe Calculator do
before do
@calculator = Calculator.new
end
it "can add two numbers" do
@calculator.add(2, 3).should eq(" 5")
end
it "pads output to eight characters" do
@calculator.add(2, 2).should eq(" 4")
@calculator.add(5, 5).should eq(" 10")
end
end
60. Unit tests
$ rspec --format doc calculator_spec.rb
Calculator
can add two numbers
can subtract two numbers
pads output to eight characters
Finished in 0.00094 seconds
3 examples, 0 failures
62. A good test suite…
• Expresses the programmer’s intent
63. A good test suite…
• Expresses the programmer’s intent
• Gives confidence that the system works
64. A good test suite…
• Expresses the programmer’s intent
• Gives confidence that the system works
• Runs quickly
65. A good test suite…
• Expresses the programmer’s intent
• Gives confidence that the system works
• Runs quickly
• Gives clear failure messages
66. A good test suite…
• Expresses the programmer’s intent
• Gives confidence that the system works
• Runs quickly
• Gives clear failure messages
• Is well-maintained
67. A good test suite…
• Expresses the programmer’s intent
• Gives confidence that the system works
• Runs quickly
• Gives clear failure messages
• Is well-maintained
• Isolates each area under test
71. Benefits of TDD
• Less manual testing required
• Faster feedback
• Safety net to make changes safer
72. Benefits of TDD
• Less manual testing required
• Faster feedback
• Safety net to make changes safer
• Shorter cycle time
73. Benefits of TDD
• Less manual testing required
• Faster feedback
• Safety net to make changes safer
• Shorter cycle time
• Reduced rework
74. Benefits of TDD
• Less manual testing required
• Faster feedback
• Safety net to make changes safer
• Shorter cycle time
• Reduced rework
• Improved design
77. What about testers?
• Less tedious repetitive manual testing
• Concentrate on exploratory testing
78. What about testers?
• Less tedious repetitive manual testing
• Concentrate on exploratory testing
• Identify edge cases and ‘sad path’ tests
79. What about testers?
• Less tedious repetitive manual testing
• Concentrate on exploratory testing
• Identify edge cases and ‘sad path’ tests
• Help define acceptance tests
81. How do I start?
• Greenfield project? JFDI! Otherwise…
82. How do I start?
• Greenfield project? JFDI! Otherwise…
• Automate highest value tests first
83. How do I start?
• Greenfield project? JFDI! Otherwise…
• Automate highest value tests first
• Important features
84. How do I start?
• Greenfield project? JFDI! Otherwise…
• Automate highest value tests first
• Important features
• Where the most bugs occur
85. How do I start?
• Greenfield project? JFDI! Otherwise…
• Automate highest value tests first
• Important features
• Where the most bugs occur
• Use TDD for new features
86. How do I start?
• Greenfield project? JFDI! Otherwise…
• Automate highest value tests first
• Important features
• Where the most bugs occur
• Use TDD for new features
• Add tests for bugs when they’re found