Best practices on designing unit tests, designing testable production code, a glimpse of TDD, using mocks and isolating pure functions for easy testing. Talk distilled from http://victorrentea.ro/#unit-testing
Held at VoxxedDays Bucharest in March 2019.
7. Victor Rentea
14 years of Java
Clean Code Evangelist
VictorRentea.ro
30+ talks, 12 meetups
.NET
Lead Architect
Tech Team Lead and Consultant
Software Craftsman
XP: Pair Programming, Refactoring, TDD
8. VictorRentea.ro @victorrentea victor.rentea@gmail.com
Independent
Technical Trainer & Coach
HibernateSpring Java 8
Architecture, DDDDesign Patterns
Clean Code Unit Testing, TDD
Java Performance and much more…Scala
180+ days1300 devs6 years
VictorRentea.rovictor.rentea@gmail.com
30 companies
Posting daily on
15. VictorRentea.ro
▪Arrange: setup the test fixture
▪Act: call the production code
▪Assert the outcomes
▪Annihilate[opt]: clean up
Terms
16 Professional Unit Testing – Basics
21. VictorRentea.ro
Sensitive
High Coverage %
Correct Tests
Specific
Expressive
Isolated & Robust
Low overlap
Fast
Few
Small, DRY tests
Test Design Goals
22 Professional Unit Testing – Principles of Test Design
= Maintainable
fail for any bug
precise failures
22. VictorRentea.ro
Test code >> Prod code
Not maintainable?
Under pressure, devs might:
23 Professional Unit Testing – Principles of Test Design
@Ignore
delete test
delete asserts
// comment test
invert asserts
// @Test
24. VictorRentea.ro
▪Code that you fear 😱
(hard to understand)
▪A distant corner in the logic
(takes 3 mins of UI clicks to reproduce)
▪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
25 Professional Unit Testing – Basics
PRIORITY
a.setName(b.getName());
*in case you're not 100% TDD
30. VictorRentea.ro33 Professional Unit Testing – Test Design Guidelines
Mutation Testing
You make some change
to the Production code
You get some "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 your test really
covers what it says?
32. VictorRentea.ro
▪Regression
- Yeehaaa! ➔ fix it
▪Change Request
- Double Check ➔ Update/Delete it ?
▪Iso-functional changes
- Superficial API Changes ➔ adjust the test
- Refactoring internals ➔ loosen the test coupling
▪A Bug in a Test
- Usually discovered via debugging ➔ fix the test
Why can a Test Fail ?
35 Professional Unit Testing – Test Design Guidelines
the usual suspect = production code
37. VictorRentea.ro
Concatenation, arithmetic
40 Professional Unit Testing – Test Design Guidelines
assertEquals("Greetings" + name, logic.getGreetings(name));
if, switch
Did you also tested that logic? ("test the tests" ☺)
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?
38. VictorRentea.ro
catch-assert
When you want to verify an exception is thrown
41 Professional Unit Testing – Test Design Guidelines
try {
logicThatShouldThrow();
fail("should have thrown");
} catch (MyException ex) {
assert("msg", ex.getMessage());
}
Can you find the bug?
39. VictorRentea.ro
Verify an Exception Type is Thrown
42 Professional Unit Testing – Test Design Guidelines
@Rule
public ExpectedException exception = none();
@Test
public void expectExceptionWithDetails() {
exception.expectMessage("contains this"); // or…
exception.expect(exceptionWithCode(FILE_LOCKED));
// throwing code
}
@Test(expected = IllegalArgumentException.class)
public void expectExceptionTypeThrown() {
// throwing code
}
Verify Exception Message/Code
For precision: let's create 20 new Exception classes !
Hamcrest
40. VictorRentea.ro
Junit 5
43 Professional Unit Testing – Test Design Guidelines
@Test
public void assertException() {
IllegalArgumentException ex = assertThrows(
IllegalArgumentException.class,
() -> throwingProdCode());
assertEquals(FILE_LOCKED, ex.getErrorCode());
}
41. VictorRentea.ro
for loop
Testing a set of input-output pairs?
➔ Parameterized Unit Tests
➔ .feature files
46 Professional Unit Testing – Test Design Guidelines
WHY?!
42. VictorRentea.roProfessional Unit Testing – Test Design Guidelines
@RunWith(Parameterized.class)
public class FizzBuzzTest {
private final int number;
private final String expected;
public FizzBuzzTest(int number, String expected) {
this.number = number;
this.expected = expected;
}
@Parameters(name="For {0}, returns "{1}"")
public static List<Object[]> getParameters() {
return asList(
new Object[] {1, "1"},
new Object[] {2, "2"},
new Object[] {5, "Buzz"},
new Object[] {3*5, "Fizz Buzz"},
new Object[] {3*7, "Fizz Wizz"},
[...]
);
}
@Test
public void checkNumber() {
assertEquals(expected, FizzBuzz.getNumber(number));
}
}
(instead of asserting in a loop)
ParameterizedTests
47
44. VictorRentea.ro
multi-thread
Looking for a race bug with sleep(random) ?
➔ Use-Your-Brain™ instead
51 Professional Unit Testing – Test Design Guidelines
asserting time
random
Testing to find errors?
➔ replay real calls from production
(Even more: Golden Master Technique)
45. VictorRentea.ro52 Professional Unit Testing – Test Design Guidelines
dependencies
TimeMachine.setTestClockAt(time);
actual = callProd(…);
assertEquals(new Date(), actual);
assertEquals(time, actual);
fails/passes randomly!
return TimeMachine.now();
return new Date();
return LocalDateTime.now();+1ms?
asserting time
your custom class
Let's truncate! :D
draconian
PowerMock?
48. VictorRentea.ro
GREEN
ZONE
Integration vs Unit Tests
57 Professional Unit Testing – Test Design Guidelines
Deep Testing
through many layers
Fragile
(DB, files, external systems)
Their failure is not always a bug
Super-Fast
100 tests/sec
(some can be slower)
Slow
start-up
the container
In-mem DB
49. VictorRentea.ro
Sensitive
High Coverage %
Correct Tests
Specific
Expressive
Isolated & Robust
Low overlap
Fast
Few
Small, DRY tests
Test Design Goals
60 Professional Unit Testing – Principles of Test Design
50. VictorRentea.ro
Minimize Duplication
61
public void myTestRaw() {
Order o = new Order();
o.setDate(new Date());
o.setLines(new ArrayList<OrderLine>());
OrderLine ol1 = new OrderLine();
ol1.setProduct("P1");
ol1.setItems(3);
o.getLines().add(ol1);
OrderLine ol2 = new OrderLine();
ol2.setProduct("P2");
ol2.setItems(null);
o.getLines().add(ol2);
target.process(o);
}
Data Initialization
51. VictorRentea.ro66 Professional Unit Testing – Test Design Guidelines
public void myTestWithBuildersAndObjectMother() {
Order o = anOrderWithOneLine()
.with(anOrderLine().withItem(anItem()))
.build();
target.process(o);
}
Fluent Builder
Object Mother
52. VictorRentea.ro
▪Fluent Builder®
▪Factory methods
- Object Mother shared between tests ?
▪Wrap production API
- In a more testable API
▪Keep common stuff in fields
▪Shared @Before
DRY Tests
67 Professional Unit Testing – Test Design Guidelines
Safe
Each @Test runs on a
fresh instance of the Test class
53. VictorRentea.ro
Sensitive
High Coverage %
Correct Tests
Specific
Expressive
Isolated & Robust
Low overlap
Fast
Few
Small, DRY tests
Test Design Goals
68 Professional Unit Testing – Principles of Test Design
54. VictorRentea.ro
@Before
You want to shrink a large test
Abusing @Before
==Practical Guide☺==
69 Professional Unit Testing – Test Design Guidelines
You move some code in the @Before
▪ Data initialization
▪ Mocks
▪ Infra setup: DB, ..
Other tests do the same:
And time passes by
When you read a test:
▪ Arrange = method + @Before
▪ What part of it ?
55. VictorRentea.ro
SingleTestClass ATest
BTest
Fixture-Based Test Breakdown
70 Professional Unit Testing – Test Design
@Before
Nested Test Fixtures in JUnit5:
https://junit.org/junit5/docs/current/user-guide/#writing-tests-nested
@Test
@Test
@Test
sharedA
@Test
@Test
@Test
@Test
@Test
@Before
@Test
@Test
@Test
@Test
@Test
@Test
@Test
@Test
@Before
sharedB
56. VictorRentea.ro
Start with one Test class/Prod class
- If it grows (>300 lines?) or
- If many tests share a fixture
Split it
71 Professional Unit Testing – Test Design Guidelines
RecordRepositoryTest
RecordRepositorySearchForVersionTest
Maybe also split
the prod code ?
57. VictorRentea.ro
5-10 lines
Descriptive names
Test Methods
72 Professional Unit Testing – Test Design Guidelines
public void givenAnInactiveCustomer_whenHePlacesAnOrder_itIsActivated()
public void getRecordDeltasForVersionWithDeletedRecord()
public void givenAProgramLinkedTo2Records_whenUpdated_2AuditsAreInserted(
RecordRowValidatorTest.invalidRecordStartDateFormat()
60. VictorRentea.ro
Suggestive Literals
75 Professional Unit Testing – Test Design Guidelines
@Test
public void customerEmailIsUpdated() {
Long customerId = createCustomer(new Customer("oldPhone"));
updateCustomerPhone(customerId, "newPhone");
Customer updatedCustomer = getCustomer(customerId);
assertEquals("newPhone", updatedCustomer.getPhone());
}
61. VictorRentea.ro
Assert a collection
Beautiful Failure Messages
(an example)
76 Professional Unit Testing – Test Design Guidelines
assertEquals(1, names.size());
assertEquals("name", names.iterator().next());
assertEquals(Collections.singleton("name"), names);
vs
62. VictorRentea.ro
Try to name this test:
➔ Break it in 2 tests
(perhaps break the Prod too? ~ CQS)
Single Assert Rule
one feature/test
77 Professional Unit Testing – Test Design Guidelines
assertEquals("FN", customer.getFirstName());
assertEquals("LN", customer.getLastName());
assertEquals(Status.SUCCESS, result);
verify(emailService).sendEmail(anyObject());
63. VictorRentea.ro
Sensitive
High Coverage %
Correct Tests
Specific
Expressive
Isolated & Robust
Low overlap
Fast
Few
Small, DRY tests
Test Design Goals
78 Professional Unit Testing – Principles of Test Design
65. VictorRentea.ro
Fresh code:
Test through the public API
Tests won't break when implem changes
Practices the API
Legacy code:
Breaking encapsulation
is a core technique
Temporarily use package/public protection
80 Professional Unit Testing – Test Design Guidelines
66. VictorRentea.ro
You run your test suite
Test #13 is RED
To debug it, you run it alone:
Test #13 is GREEN
??!!!?!
Yet Another Happy Day#2…
81 Professional Unit Testing – Testing on DB
67. VictorRentea.ro
You run your test suite
It's GREEN
You add Test #43
Test #13 turns RED
(unchanged!)
You delete Test #43
Test #13 goes back GREEN
Yet Another Happy Day#3…
82 Professional Unit Testing – Testing on DB
??!!!?!
68. VictorRentea.ro
▪In-memory Objects
▪COMMITs to DB
▪External files/systems
Hidden Test Dependencies
83 Professional Unit Testing – Testing on DB
- Static fields, singletons
- PowerMocks
- Thread Locals
- Container shared by tests [Spring]
- In-memory or external
➔ Reset them in @Before
69. VictorRentea.ro
JUnit runs tests in unexpected order
To Ensure Independency
84 Professional Unit Testing – Testing on DB
(DON'T DO THIS)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
70. VictorRentea.ro
Sensitive
High Coverage %
Correct Tests
Specific
Expressive
Isolated & Robust
Low overlapping
Fast
Few
Small, DRY tests
Test Design Goals
86 Professional Unit Testing – Principles of Test Design
72. VictorRentea.ro
A discipline in which
you grow software in small increments
for which you write
the tests before the implementation
TDD
88 Professional Unit Testing – TDD
Credited to Kent Beck, 2003
73. VictorRentea.ro
TDD Mantra
Red-Green-Refactor
90 Professional Unit Testing – TDD
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 PassWrite a Failing Test
for a new feature
Refactor
prod + tests
pass
fail
Run
Tests
Run
Tests
Run
Tests
1-10 min
Overlapping? –
Incorrect? –
Rules of Simple
Design
74. VictorRentea.ro
Rules of Simple Design
91 Professional Unit Testing – TDD
- Kent Beck
https://martinfowler.com/bliki/BeckDesignRules.html
2. Reveals Intention
3. No Duplication
4. Fewest Elements
1. Passes the Tests
77. VictorRentea.ro
Fake Objects
102 Professional Unit Testing – Mocking
Fast
DB ☺
Repeatable
Isolation from external systems
Mentally Simpler
To test a layer, fake the layer bellow:
WHY?!
79. VictorRentea.ro
Fake
104 Professional Unit Testing – Mocking
Mock
Verify a method call
verify(repo).save(…);
Stub
Respond to method calls
when(mock.method())
.thenReturn(…);
The confusion:
81. VictorRentea.ro
Side-effects
1)On external systems:
2)On in-memory objects
➔ assert their state
What can a Unit Test check?
107 Professional Unit Testing – Mocking
Input OutputFeature
(prod function)
Data from
dependencies
Input+Deps → Output
(real or stubbed)
a)Real: SELECT
b)Mock: verify repo.save() was called
Input → Output
Simple and Elegant
Pure function: 𝑓 𝑥 = 𝑥2
Simplify design:
purify logic
85. VictorRentea.ro
Verify every method call
How many times calls happen
times(1)
No extra call happen
Capture-assert every argument
115 Professional Unit Testing – Mocking
89. VictorRentea.ro
Which API is stable?
Yet reachable?
Where to Slice it?
121
A
B
C
D
nearest edge
final real system
intermediary
TEST
90. VictorRentea.ro
It may be simpler to test
some classes together!
122
(versus testing them separately using mocks)
91. VictorRentea.ro127 Professional Unit Testing – Mocking
OrderService
EmailService
PARTIAL MOCK
(normal)MOCK
private sendEmail() public sendEmail()
Separation by Layers of Abstraction
A Tale about the Evil Partial Mock...
Refactor
using
mocks
Which methods
are more abstract ?
(i.e. higher-level)Don’t do that !!
Partial Mocks are Evil ! Oh!.. Why?
Dunno..
@Autowired
Tests
overlap!!
public placeOrder()
public shipOrder()
a.k.a. “spy”
99. VictorRentea.ro
▪Extract-'n-test
- Then mock it out
▪Break encapsulation
- Expose internal state
- Spy internal methods
▪Hack statics
- PowerMock methods
- Control global state
▪Introduce Seams
- Decoupling places
▪Exploratory refactoring
- 30 mins that you always throw away
- Search for ways to break the design
to become more testable
▪IDE Refactoring only
- When not guarded by tests
- Mob
▪Temporary ugly tests
▪Unit Test + Refactoring
- Always together!
Some Techniques
for Testing Legacy Code
150 Professional Unit Testing – Legacy Code More on: http://blog.adrianbolboaca.ro/2015/02/unit-testing-on-legacy-code/
102. VictorRentea.ro
Testing Legacy Code
Golden Master Technique
154 Professional Unit Testing – Recap
http://blog.adrianbolboaca.ro/2014/05/golden-master/
original
refactored
same
input
same
output
same interactions
Idea: record these in production,
then replay them on your refactor
103. 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
Martin Fowler, 1999
References
156 Professional Unit Testing – Further Reading
104. 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
157
105. VictorRentea.ro158
End of Part 1
Trainings, talks, goodies Daily posts on
Let's chat!
What do you want after the break?
Proxies; Legacy Kata; TDD; Java8
Vote here: victorrentea.ro/vote
Come take 1 sticker: