2. UNIT TESTING & TDD
Xavi Hidalgo - CTO at Apiumtech
xavi.hidalgo@apiumtech.com
@xavi_reloaded
3. About us
A team of designers, developers and software architects offering multi-disciplinary services in software
development with +15 years of experience
4. Write a test that fails
Eliminate redundancy Make the code work
Test driven development
Faster & easier
It will make it easier & faster for you to add new functionalities
Identify code errors
You will be able to identify code errors in a short period of time
Extensible code
You will be able to have more modularized, flexible & extensible code
New business requirements
Update your software to address new business requirements
and increase your capacity to react
5. A bit of history
timeline of TDD
1989
1994 1995
1999 2000 2001 2002 2006
2011 2014
First
Testing
Framework
KentBeck
Kent Writes
SUnit
Firsts demos of TDD by
Kent.
Extreme Programming
Refactoring Book.
Extreme Programming
Book.
First TDD Tutorial.
Mock Objects.
Junit, Nunit,
Continuous
Integration.
CruiseControl.
KentBeck
TDD Book
BDD
DanNorth
RyanDahl
Node.js
Test Frameworks
for front-end
KentBeck
MartinFowler
David HH
Is TDD Dead?
Hangouts
9. The pragmatic programmer
Programmers are constantly in maintenance mode
A closed, fixed-requirements system is a programmers’ chimera:
● Requirements always change on the way (unexpectedly & unpredictably).
● If not, you always discover new requirements as you dig into the system and the code. This implies change.
● If not, the system needs to be bug-fixed, which implies change.
10. Predictive vs. adaptive
● Predictive
Long-phase of up-front design with quasi-fixed requirements. Try to foreseen future requirements and to give
flexibility to the whole system.
● Adaptative
Assume change and evolve the system, development tools and even the methodology to adapt to change. Don’t
try to anticipate the future, cause you just can’t.
“build for change, not for future”
(eXtreme Programming eXamined)
11. Design principles in Agile
DRY YAGNI KISS
Don’t repeat yourself You aren’t gonna need it Keep it simple stupid
Or equivalents:
Say it Once and only once
Choose the Simplest strategy that could possibly work
“keep it DRY, shy and tell the other guy” from the authors of Pragmatic Programmer
Emergent/evolutionary design
12. Enabling practices from XP
Enabling
Practices that enable an evolutionary design when not used, the result of evolutionary design is always entropy.
Engineering Practices (in order of application)
● Unit Testing (allows:)
● Refactoring
● Automated Build
● Continuous Integration
● Continuous design
● Emergent architecture
● Pair Programming
Refactoring is forbidden without tests; high risk with none business value
Without Unit Testing there’s no evolutionary design
13. Agile learning steps
from osherove
Meanwhile, remove duplication, be DRY!
1. Learn Good
Unit Testing
2. Learn Test
Driven
Development
3. Learn
S.O.L.I.D. &
advanced OO
design
principles
14. What is not unit testing?
Typical misconceptions of unit testing
Unit test, but… Are we testing as a unit?
15. What is a (good) unit test?
1
● It is automated and repeatable
● It is easy to implement… and maintain!
● Once it’s written, it stays on for the future
● Anyone can run it
● It runs at the push of a button
● It runs quickly
17. What is a (good) unit test?
2
Definition of a Good Unit Test
A unit test is an automated piece of code that invokes a
different method and then checks some assumptions about
the logical behavior of that method or class under test.
A unit test is written using a unit testing framework. It can
be written easily and runs quickly. It can be executed,
repeatedly, by anyone on the development team.
18. Basic terminology
● A (unit) test - a test method
● Production Code - what is not Test Code
● Fixture - all the things we need to have in place in
order to run a test
● SUT - System Under Test
● SetUp/Exercise/Verify/TearDown - Four phases
test (xUnit standard), sometimes called
Arrange/Act/Assert
● Red/Green bar - red if one of the current set of
test detects an error
● Test failure - when a test gives red bar
19. xUnit standard
unit test example
Assert means to throw an exception if a condition is not verified Teardown only for things that could
persist over test methods e.g. Database fixture, singletons, etc.
Naming Convention
SetUp
Exercise
Verify
23. Goals of test automation
Tests should help us improve quality
● Test as specification
● Bug Repellent
● Defect Localization
Tests should help us understand the SUT
● Test as documentation
● Test as exploration of T.P. APIs
Tests should reduce (and not introduce) Risk
● Tests as safety net (In legacy code)
● Do Not Harm (Don’t modify the SUT (intrusively))
24. Goals of test automation
Tests should be easy to run
● Fully automated test (setUp fixture, execute, CLEAN UP!)
● Self-checking tests
● Repeatable (deterministic) test
● Quick test!
Tests should be easy to write and maintain
● Simple tests
● Expressive tests
● Separation of concern
Tests should require minimal maintenance
as the system evolves around them
● Robust Test
● Test Code is just Code!!!
25. Some test smells
Indirect Testing
● Unclear
● Not exhaustive in boundary condition
● Fragile Test (low defect localization)
Conditional Test Logic
● Unclear
● Bugs in test code
● Forbidden!
Only happy-path test
● When you never test:
○ Boundary condition
○ Exception throwing and control flow
Test Code Duplication
● High test maintenance cost
● Obscure test (too much code)
● Same problems as production code duplication
(copy-paste)
○ Duplication is THE enemy!
○ Test Code is Just Code! (you’ll maintain
it, debug it, etc.)
Test Logic in Production
● Bugs in production
26. Some test smells
Assertion Roulette (many assertions in a
test method)
● When a test fails, I have to manually check which
assertion failed
(better look only at the method name)
● When an assert fails, the following assertions are
not run
● Breaks the rule of Defect Localization
Erratic Test
● Break repeatable-deterministic test rule
○ Are tests isolated from each-others?
○ Are tearDowns deterministic?
● Throws away all benefits of automatic test
○ Manual intervention to check why a test is failing
○ Low programmer’s confidence in test suite
27. Some test smells
Interacting Tests
● I have a lot of tests failing and I don’t know the cause
○ I have to check it manually test-by-test
● Breaks the rule of Defect Localization
28. Some patterns
The simplest test strategy which could possibly work
If you’re losing too much time on a test, you are usually mistaken one of the two:
● The design of the SUT
● The design of the test (testing strategy)
Fresh Fixture
● Each test sets up a fixture for itself
● Each test cleans up its fixture after
29. Some patterns
Test helper method/class
● Extract common test logic (es. setUp logic) in common code module
Minimal Fixture
● Easy to read
● Keep the fixture as little as possible
○ Put in setUp method only the common fixture for every test in the test class
○ All that is not common goes in the test method
■ Extract helpers if you have duplicated code
● Build only what is needed to write the test
● Design the SUT to have little dependencies
30. Pattern: test doubles
fakes, mocks, stubs
Why
● Isolate the SUT
What
● Replace dependency with a “fake/test one”
● NO conditional logic but:
○ Replace dependency by polymorphism
○ Override protected method (extract & override)
○ (In scripting) directly override dependencies in test
When
● Existing code with many dependencies, difficult to isolate
● I have to rely on a component which doesn’t still exist
● Difficult setUp/verification because of third-part
framework
○ Asynchronous
○ UI objects
○ Configuration
● Real dependency is slow
● It Is the simplest strategy
31. Pattern: Parameterized-Tabular test
Why
● Reduce test code duplication
● Make it easier to add a test case
What
When
● Whenever it’s possible to abstract a meaningful
test-logic
● Typical case I found: test a regular expression
32. Unit testing benefits
● Continuous and quick regression test
“adverse effects” found earlier
● Defect Localization
See for example integration scenarios
● Test as documentation
● Reliable and direct testing
It’s DIRECT against a unit of logic code
It’s much quicker than UI tests
● Sensation of being confident
● Allows refactoring
● Allows CI
● Helps APIs/Legacy code exploration (and doc)
● Quantification of work DONE (Code that works)
● Easier to find and fix boundary conditions into the suite
● More..?
Results in Agile
Response to change!
33. ● The main test metric is the “Test Coverage”
○ The amount of lines of code executed by the
tests over the total
● Coverage Tools
● 100% coverage is impossible
○ You don’t test Getter & Setter
○ You test only what can fail
○ In my opinion, it’s not so important as a number
● In my experience, 80% coverage is very good
Unit testing metrics
34. Unit testing economics
UT and TDD are productivity development tools, NOT (only) quality or testing tools.
36. Write a test that fails
Eliminate redundancy Make the code work
Test driven development
Faster & easier
It will make it easier & faster for you to add new functionalities
Identify code errors
You will be able to identify code errors in a short period of time
Extensible code
You will be able to have more modularized, flexible & extensible code
New business requirements
Update your software to address new business requirements
and increase your capacity to react
37. Test driven development
TDD
How we test the test?
● Write a test for the test? Then write a test for the test of the test?
● Write test code first!!
TDD means:
● Write a test for the required functionality
● Run the test and verify the red bar
● Implement the functionality as simply as you can
● Run the test and verify the green bar
● Safely refactor by continuously running tests
Keep the bar green!
“TDD is the Backbone of eXtreme Programming”
38. GOALS
Clean Code that
works, Now!
RULES
1. Write new code only if an
automated test has failed
2. Eliminate duplication
PRACTICE
Small cycles of
red/green/refactor
TDD principles
48. Test driven development
Benefits (1)
The same unit testing benefits, plus:
Test the test code
Improve the design of the code
● You design as a user of a class
● You design for testability from the beginning
● You usually design by contract (see Test Doubles)
● TDD is a Design Tool, NOT a testing Tool
Tests replace specifications
● You design while coding and incrementally
● With starting with simplest cases:
○ Easier to find boundary conditions
Rapid feedback
● You save the time of manual testing during development
● You receive the feedback of your design in minutes, not in months
● Feedback during development, not at the end of the class
49. Test driven development
Benefits (2)
Unforeseen condition are often found earlier
● During first steps of development
● Always starting with the simplest case, you find early boundary conditions
Better understanding of requirements
● You are forced to dig into requirements one-by-one
If you do it first, you never forget
Development Pace (test-by-test) and Morale Boost
Naturally high Code Coverage
● You don’t write anything if there’s not a test in red bar
● There aren’t functionality without test – all is documented by a test
50. Test driven development
Benefits (3)
Focuses on functionality first - gives a safe space for refactoring
● Helps to avoid YAGNI traps
● The code is kept clean by refactoring
● You refactor under the safety net
● There’s not the need for planning a refactoring phase, refactoring is done continuously and safely
● Red/green - “that works”
● Refactor - “clean code”
51. Test driven development
Benefits (4)
Bugs are
1. Not introduced
2. Found in the early steps during
development
“TDD is the cheapest way to find bugs as
early as possible”
52. Test driven development
Bug fixing
Application of TDD to bug fixing
When you understand a bug:
● Write a UT to reproduce it automatically
● Verify UT gives red bar
● Only then fix the bug
● Verify UT gives green
● Run all tests to verify you didn’t break anything
Never look behind!
● A fixed bug will never come back
● Tests are the history and documentation of application bugs
● Each new functionality has to be compliant with the bug fixing
Good point to start with TDD
Be integralist!!
● I don’t pass the fix into production without a test
53. “Sounds good, but our situation is different...”
part 1
Greenfield vs. Brownfield development
Code difficult to understand
Code which is not in OO manner
Code poorly encapsulated/not clean
Code difficult to change
Code in a language I don’t know
Code without documentation
Code not written by me
Code that … Suggestions?
What does it mean:
Legacy code?
54. Legacy code
Code without
Unit Test
No matter how clean it is if I cannot change it safely
Two strategies:
1. Edit and pray
2. Cover and modify
But you can’t dedicate too much to write test for
the whole system, can you?
“Sounds good, but our situation is different...”
part 2
55. UT/TDD legacy code
build a safety net first
● Cover with some happy-path integration tests only the functionality you’re going to change
● Do little refactoring only for testability where needed
○ Introduce seams (not intrusive observation points)
● Cover with unit tests only the spots affected by changes I have to make
● Write the UT of the change you’re going to do
● Change and refactor
It’s not costless, but it’s cheaper than a bug & much less risky than a rewrite.
It had been cheaper to think it before, when it was greenfield, but only steps ahead!
56. Manual test is still indispensable
● Human interactions and validation
● Functional feedback
● Integration tests difficult to automate
● Manual test has a completely different role:
● Remember: UT & TDD are design/develop tools, NOT testing
tools
Then what is the great gain? (see above and previous)
● All that you can validate automatically you do
● Manual test is executed only few times – it’s the last validation
In legacy code phase accurate Manual Tests are still needed
NO
Are UT sufficient to replace manual test?
57. Last principles
Not revolutions, but daily practices
Weakest link principle:
● The bug will enter where there’s no coverage
○ We have to be constructive
○ Introducing order is a shared task
○ Each new line of code not under test is:
■ Brand new legacy code
■ Going to generate troubles to you and your workmates
■ Breaks the safety net
● The code ownership is shared among the team
○ There’s NOT someone’s code
Don’t introduce Technical Debt
64. Essential bibliography
[Osherove, R., The Art of Unit Testing, 2009]
[Meszaros, G., xUnit Test Patterns, 2007]
[Beck, K., Test Driven Development by Example, 2002]
[Fowler, M. Beck, K., Refactoring, 1999]
[Osherove, R., Iserializable – Roy Osherove Blog, http://weblogs.asp.net/rosherove/]
[Meszaros, G., http://xunitpatterns.com/]
[Kniberg, H., Scrum and Xp from the trenches, 2007]
[Succi et al., eXtreme Programming eXamined, 2001]
[Feathers, C., Working Effectively with Legacy Code, 2004]
[Hunt, A. et al., The Pragmatic Programmer, 1999]