Slides of the Talk I gave at Devoxx Belgium 2019.
=== Abstract ===
Focusing on the creative work without being terrified of breaking the existing behavior can make software development very addictive! Good automated tests can buy you that!
However, if your tests are not maintainable they may end up slowing you down and causing you painful headaches, compilation errors and spurious failures. To avoid that, your unit tests should be significant; expressive; clean; DRY; non-overlapping; and blazing fast. Writing good tests becomes the toughest challenge for any developer, no matter how battle-hardened: you need to balance risk with test maintenance costs, while looking out for test design smells that call for [risky] refactoring to drive your design towards a set of key principles (included:).
Principles that will end up shaping the way you craft the Production code itself. Because in the end, a good, clean design is more important than coverage%.
But testing gives you the best feedback to get there.
Grab a black coffee and join this snippet from Victor’s Pro Unit Testing #training, to learn about testing priorities, buggy tests, the shared @Before, Mocks vs Stubs and how to reduce them by "purifying" your logic, testing Legacy Code and refactoring @Spy-es out.
All of that in an entertaining, dynamic and memorable session.
A Secure and Reliable Document Management System is Essential.docx
The Art of Unit Testing - Towards a Testable Design
1. The Art of Unit Testing
The Circle of Purity
Check out my ‘Clean Code – The Next Episode’
Deep Dive at Devoxx BE 2019 for more context
victor.rentea@gmail.com ♦ ♦ @victorrentea ♦ VictorRentea.ro
12. 19 VictorRentea.ro
Sensitive
High Coverage %
Correct Tests
Specific
Expressive
Isolated & Robust
Low overlap
Fast
Few
Small, DRY tests
Test Design Goals
= Maintainable
fail for any bug
precise failures
13. 20 VictorRentea.ro
Test code >> Prod code
Not Maintainable?
Under pressure, developers will:
@Ignore
delete test
// comment test
// @Test
delete asserts
invert asserts
EVIL
15. 22 VictorRentea.ro
▪Code that you fear 😱
▪A distant corner in the logic
▪A bug (before fixing it) 🏆
▪for/if/while ⚙
▪Thrown exception
▪A method that just calls two others
▪Trivial code
▪Legacy code with 0 bugs, 0 changes
(over the last year)
Testing Priorities
PRIORITY
a.setName(b.getName());
*in case you're not 100% TDD
hard to understand
takes 3 mins of UI clicks to reproduce
+0.01%
23. 32 VictorRentea.ro
Mutation Testing
You break the
Production code
You get a "mutant" code
The tests should squash
the mutant by turning red
You run the Tests You undo the change
There are frameworks that play like this with your code,
but their results might be depressing
Tests fail ֞ Production code is altered
How can you check
that a test really covers
what it claims?
25. 34 VictorRentea.ro
▪Regression Bug
- Yeehaaa! ➔ fix it
▪Deep Change Request
- Double Check ➔ Update/Delete the test?
▪Iso-functional changes
- Superficial API Changes ➔ adjust the test
- Refactoring internals ➔ test is too tightly coupled
▪A Bug in a Test
- Usually discovered via debugging
Why can a Test Fail ?
the usual suspect = production code
https://www.monkeyuser.com/
30. 39 VictorRentea.ro
SimpleTests!
A bit of logic in a test
assertEquals("Greetings" + name, logic.getGreetings(name));
it's missing a " "!
Why? The same Dev wrote both prod and test code! ☺ CPP
➔ Prefer literals
assertEquals("Greetings John", logic.getGreetings("John"));
Can you find the bug?
35. 53 VictorRentea.ro
random
Testing to find errors?
➔ replay real calls from production
(Even more: Golden Master Technique)
SimpleTests!
https://adrianbolboaca.ro/2014/05/golden-master/
36. 54 VictorRentea.ro
callProd(…)
dependencies
TimeMachine.setTestTime(time);
actual = callProd(…);
assertEquals(new Date(), actual);
assertEquals(time, actual);
TimeMachine.clearTestTime(); // *
fails/passes randomly!
return TimeMachine.now();
return new Date();
return LocalDateTime.now();
+1ms?
asserting time
your custom class
Let's truncate! :D
draconian PowerMock?
* See here my auto-cleaning JUnit4 Time @Rule
,now
39. 59 VictorRentea.ro
GREEN
ZONE
Integration vs Unit Tests
Deep Testing
through many layers
Fragile
(depends on DB, files, other systems)
Such a failed test ⇒ a bug
Super-Fast
100 tests/sec
(some can be slower)
Slow
starts-up
the container
In-mem DB
43. 68 VictorRentea.ro
public void myTestWithBuildersAndObjectMother() {
Order o = anOrderWithOneLine()
.with(anOrderLine().withItem(anItem()))
.build();
target.process(o);
} Object Mother®
Fluent Builder®
Any use of ® in my talks is anecdotal
44. 69 VictorRentea.ro
▪Fluent Builder®
▪Factory methods
- Object Mother shared between tests ?
▪Wrap production API
- In a more testable DSL
▪Keep common stuff in fields
▪Shared @Before
DRY Tests
Safe
Each @Test runs on a
fresh instance of the Test class
All use of ® in my talks is anecdotal
45. 71 VictorRentea.ro
You want to shrink a large test
Abusing @Before
==Practical Guide☺==
Other tests do the same:
Time passes by
When you read a test:
▪ Arrange = method + @Before
▪ What part of it ?
You move some code in the @Before
▪ Mocks▪ Data Init ▪ Infra setup: DB, ..
47. 73 VictorRentea.ro
Start with one Test class/Prod class
- If it grows (>300 lines?) or
- If many tests share a fixture
Split it
RecordRepositoryTest
RecordRepositorySearchForVersionTest
Maybe also split
the prod code ?
1DESIGN INSIGHT
48. 74 VictorRentea.ro
5-10 lines
Descriptive names
Test Methods
public void givenAnInactiveCustomer_whenHePlacesAnOrder_itIsActivated()
public void getRecordDeltasForVersionWithDeletedRecord()
public void givenAProgramLinkedTo2Records_whenUpdated_2AuditsAreInserted()
RecordRowValidatorTest.invalidRecordStartDateFormat()
58. 85 VictorRentea.ro
You run your test suite
Test #13 is RED
To debug it, you run that test:
Test #13 goes GREEN
??!!!?!
Yet Another Happy Day#2…
59. 86 VictorRentea.ro
You run your test suite
It’s all GREEN
You add Test #43
Test #13 turns RED
(untouched!)
You delete Test #43
Test #13 goes back GREEN
Yet Another Happy Day#3…
??!!!?!
60. 87 VictorRentea.ro
@Before
▪In-memory Objects
▪COMMITs to a DB
▪External files/systems
Hidden Test Dependencies
- Static fields, singletons
- PowerMocks
- Thread Locals
- Container shared by tests [Spring]
- In-memory
- External (real)
Reset them in @After
➔ @DirtiesContext
➔ Isolated tests ran in Tx rolled back after the test
Intermediate
Commits
61. 88 VictorRentea.ro
JUnit runs tests in unexpected order
To Ensure Independency
(DON'T DO THIS)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
Surprise me!
63. 92 VictorRentea.ro
A discipline
in which you grow software
in small increments
for which you write
the tests before the implementation
Test-Driven Development
Credited to Kent Beck, 2003
64. 93 VictorRentea.ro
1. Write prod code only to fix a failing test
- Although we often foresee the implementation
2. Write 1 test for a feature not yet implemented
- It must fail with grace
3. Write just enough prod code
to pass the test
The 3 Rules of TDD--Uncle Bob
Code Golf
Fewest keystrokes
to make it pass
few
65. 94 VictorRentea.ro
Red-Green-Refactor
With Grace
fail
KISS, YAGNI, Golf:
Write as little prod code,
don’t mind about quality
You can't break anything
•Start with the simplest cases
•Start with the ☺ flows
If you get stuck or
other tests break
=> revert and retry
DRY, Clean Code
Write prod code to
Make It Pass
Write a Failing Test
for a new feature
Refactor
prod XOR tests
pass
fail
Run
Tests
Run
Tests
Run
Tests
1-10 min
Incorrect? –
Overlapping? –
(could keep for documenting)
66. 95 VictorRentea.ro
Rules of Simple Design
https://martinfowler.com/bliki/BeckDesignRules.html
2. Reveals Intention
3. No Duplication
4. Fewest Elements
1. Passes the Tests
67. 96 VictorRentea.ro
TDD Schools of Thought
TDDasifyoumeantit
A comparative example:
Uncle Bob (Chicago School) vs Sandro Mancuso (London School): git has two branches.
https://gist.github.com/xpepper/2e3519d2cb8568a0b13739d9ae497f21
aka Chicago school aka London school
68. 101 VictorRentea.ro
Invest any effort necessary to
create Unit Tests
for any tough problem you face
One of the best way
tests save you time!
73. 108 VictorRentea.ro
Fake Objects
Fast
DB ☺
Repeatable
Isolation from external systems
Mentally Simpler
To test a layer, fake the layer bellow:
WHY?!
Cheating?
James Coplien: https://rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf
77. 113 VictorRentea.ro
Side-effects
1) On external systems:
2) On in-memory objects
➔ assert their state after
What can a Unit Test check?
Input OutputFeature
(prod function)
Data from
dependencies
Input+Deps → Output
(real or stubbed)
a) Real: SELECT
b) Mocked: verify repo.save() was called
Input → Output
Simple and Elegant Tests
Pure Function: 𝑓 𝑥 = 𝑥2
Simplify design:
purify logic
Philosophic Slide
“Interaction Testing”
96. 163 VictorRentea.ro
▪Extract-'n-test
- Then mock it out
▪IDE Refactoring
- When not guarded by tests; mob refactoring
▪Frequent Commits
▪Break encapsulation
- Expose internal state
- Spy internal methods
▪Hack statics
- PowerMock methods (still, avoid…)
- Control global state
▪Copy, don’t Cut
- Make old code useless while still compiling
▪Look for Seams
- Decoupling places
▪Exploratory refactoring
- 30 mins that you always throw away at the end
- Seek how to make the design testable
▪Temporary ugly tests
▪Unit Test + Refactoring
- Go together
Legacy Testing Techniques
More on: http://blog.adrianbolboaca.ro/2015/02/unit-testing-on-legacy-code/
The one slide with
98. 167 VictorRentea.ro
▪ Clean Code + Clean Coders Videos [CCEp#] http://www.cleancoders.com/
Robert C. Martin (Uncle Bob)
▪ (London-Style TDD): Growing Object-Oriented Software, Guided by Tests
Steve Freeman, Nat Pryce, 2009
▪ (Classic-Style TDD): Test-Driven Development, by example
Kent Beck, 2000
▪ The Art of Unit Testing
Roy Osherove, 2009
▪ Coding Dojo Handbook
Emily Bache, 2009
▪ https://springtestdbunit.github.io/spring-test-dbunit/faq.html
▪ Professional TDD with C#: Developing Real World Apps with TDD
James Bender, Jeff McWherter, 2011
▪ Agile in a Flash: https://pragprog.com/book/olag/agile-in-a-flash
▪ "Is TDD Dead?" - on You Tube
▪ Introducing BDD: http://dannorth.net/introducing-bdd/
Dan North
▪ Refactoring 2nd ed
Martin Fowler, 2018
Reading
99. 168 VictorRentea.ro
▪Test where the Fear is
▪Correct Tests fail Prod mutates
▪Explicit, Simple Tests
▪TODO: Try TDD!
▪Purify the heavy logic
▪Listen to Tests: Testable Design
Key Points
102. 172 VictorRentea.ro
Thank You!
VictorRentea.ro
Trainings, talks, goodies
@VictorRentea
Follow me for quality posts:
victorrentea.ro/community
Speaking Romanian? join me
A SCALABILITY PROBLEM:
OUT OF STICKERS
AND KEY CHEAT-SHEETS¡¡ PLEASE !!
Hunt me down for questions!
YOU CAN DOWNLOAD AND PRINT THEM
YOURSELF FROM VICTORRENTEA.RO