The Art of Unit Testing, Third Edition....1
Praise for the second edition....3
contents....9
foreword to the second edition....16
foreword to the first edition....18
preface....20
acknowledgments....22
about this book....23
Whats new in the third edition....23
Who should read this book....24
How this book is organized: A road map....24
Code conventions and downloads....25
Software requirements....25
liveBook discussion forum....25
Other projects by Roy Osherove....26
Other projects by Vladimir Khorikov....26
about the authors....27
about the cover illustration....28
Part 1Getting started....29
1 The basics of unit testing....31
1.1 The first step....33
1.2 Defining unit testing, step by step....33
1.3 Entry points and exit points....34
1.4 Exit point types....39
1.5 Different exit points, different techniques....40
1.6 A test from scratch....40
1.7 Characteristics of a good unit test....43
1.7.1 What is a good unit test?....43
1.7.2 A unit test checklist....44
1.8 Integration tests....45
1.9 Finalizing our definition....49
1.10 Test-driven development....50
1.10.1 TDD: Not a substitute for good unit tests....52
1.10.2 Three core skills needed for successful TDD....53
Summary....54
2 A first unit test....56
2.1 Introducing Jest....57
2.1.1 Preparing our environment....57
2.1.2 Preparing our working folder....57
2.1.3 Installing Jest....58
2.1.4 Creating a test file....58
2.1.5 Executing Jest....59
2.2 The library, the assert, the runner, and the reporter....61
2.3 What unit testing frameworks offer....62
2.3.1 The xUnit frameworks....64
2.3.2 xUnit, TAP, and Jest structures....64
2.4 Introducing the Password Verifier project....65
2.5 The first Jest test for verifyPassword....65
2.5.1 The Arrange-Act-Assert pattern....66
2.5.2 Testing the test....67
2.5.3 USE naming....67
2.5.4 String comparisons and maintainability....68
2.5.5 Using describe()....68
2.5.6 Structure implying context....69
2.5.7 The it() function....70
2.5.8 Two Jest flavors....70
2.5.9 Refactoring the production code....71
2.6 Trying the beforeEach() route....73
2.6.1 beforeEach() and scroll fatigue....75
2.7 Trying the factory method route....77
2.7.1 Replacing beforeEach() completely with factory methods....78
2.8 Going full circle to test()....80
2.9 Refactoring to parameterized tests....80
2.10 Checking for expected thrown errors....83
2.11 Setting test categories....84
Summary....85
Part 2Core techniques....87
3 Breaking dependencies with stubs....89
3.1 Types of dependencies....90
3.2 Reasons to use stubs....92
3.3 Generally accepted design approaches to stubbing....94
3.3.1 Stubbing out time with parameter injection....94
3.3.2 Dependencies, injections, and control....96
3.4 Functional injection techniques....97
3.4.1 Injecting a function....97
3.4.2 Dependency injection via partial application....98
3.5 Modular injection techniques....98
3.6 Moving toward objects with constructor functions....101
3.7 Object-oriented injection techniques....102
3.7.1 Constructor injection....102
3.7.2 Injecting an object instead of a function....104
3.7.3 Extracting a common interface....107
Summary....109
4 Interaction testing using mock objects....111
4.1 Interaction testing, mocks, and stubs....112
4.2 Depending on a logger....113
4.3 Standard style: Introduce parameter refactoring....115
4.4 The importance of differentiating between mocks and stubs....116
4.5 Modular-style mocks....117
4.5.1 Example of production code....118
4.5.2 Refactoring the production code in a modular injection style....119
4.5.3 A test example with modular-style injection....120
4.6 Mocks in a functional style....120
4.6.1 Working with a currying style....120
4.6.2 Working with higher-order functions and not currying....121
4.7 Mocks in an object-oriented style....122
4.7.1 Refactoring production code for injection....122
4.7.2 Refactoring production code with interface injection....124
4.8 Dealing with complicated interfaces....126
4.8.1 Example of a complicated interface....126
4.8.2 Writing tests with complicated interfaces....127
4.8.3 Downsides of using complicated interfaces directly....128
4.8.4 The interface segregation principle....129
4.9 Partial mocks....129
4.9.1 A functional example of a partial mock....129
4.9.2 An object-oriented partial mock example....130
Summary....131
5 Isolation frameworks....132
5.1 Defining isolation frameworks....133
5.1.1 Choosing a flavor: Loose vs. typed....133
5.2 Faking modules dynamically....134
5.2.1 Some things to notice about Jests API....136
5.2.2 Consider abstracting away direct dependencies....137
5.3 Functional dynamic mocks and stubs....137
5.4 Object-oriented dynamic mocks and stubs....138
5.4.1 Using a loosely typed framework....138
5.4.2 Switching to a type-friendly framework....140
5.5 Stubbing behavior dynamically....142
5.5.1 An object-oriented example with a mock and a stub....142
5.5.2 Stubs and mocks with substitute.js....144
5.6 Advantages and traps of isolation frameworks....145
5.6.1 You dont need mock objects most of the time....146
5.6.2 Unreadable test code....146
5.6.3 Verifying the wrong things....146
5.6.4 Having more than one mock per test....147
5.6.5 Overspecifying the tests....147
Summary....147
6 Unit testing asynchronous code....149
6.1 Dealing with async data fetching....150
6.1.1 An initial attempt with an integration test....151
6.1.2 Waiting for the act....152
6.1.3 Integration testing of asyncawait....152
6.1.4 Challenges with integration tests....153
6.2 Making our code unit-test friendly....153
6.2.1 Extracting an entry point....154
6.2.2 The Extract Adapter pattern....159
6.3 Dealing with timers....166
6.3.1 Stubbing timers out with monkey-patching....166
6.3.2 Faking setTimeout with Jest....167
6.4 Dealing with common events....169
6.4.1 Dealing with event emitters....169
6.4.2 Dealing with click events....170
6.5 Bringing in the DOM testing library....172
Summary....173
Part 3The test code....175
7 Trustworthy tests....177
7.1 How to know you trust a test....178
7.2 Why tests fail....178
7.2.1 A real bug has been uncovered in the production code....179
7.2.2 A buggy test gives a false failure....179
7.2.3 The test is out of date due to a change in functionality....180
7.2.4 The test conflicts with another test....180
7.2.5 The test is flaky....181
7.3 Avoiding logic in unit tests....181
7.3.1 Logic in asserts: Creating dynamic expected values....181
7.3.2 Other forms of logic....183
7.3.3 Even more logic....184
7.4 Smelling a false sense of trust in passing tests....184
7.4.1 Tests that dont assert anything....185
7.4.2 Not understanding the tests....185
7.4.3 Mixing unit tests and flaky integration tests....186
7.4.4 Testing multiple exit points....186
7.4.5 Tests that keep changing....188
7.5 Dealing with flaky tests....189
7.5.1 What can you do once youve found a flaky test?....191
7.5.2 Preventing flakiness in higher-level tests....191
Summary....192
8 Maintainability....193
8.1 Changes forced by failing tests....194
8.1.1 The test is not relevant or conflicts with another test....194
8.1.2 Changes in the production codes API....194
8.1.3 Changes in other tests....197
8.2 Refactoring to increase maintainability....201
8.2.1 Avoid testing private or protected methods....201
8.2.2 Keep tests DRY....203
8.2.3 Avoid setup methods....203
8.2.4 Use parameterized tests to remove duplication....204
8.3 Avoid overspecification....205
8.3.1 Internal behavior overspecification with mocks....205
8.3.2 Exact outputs and ordering overspecification....207
Summary....211
Part 4Design and process....213
9 Readability....215
9.1 Naming unit tests....216
9.2 Magic values and naming variables....217
9.3 Separating asserts from actions....218
9.4 Setting up and tearing down....219
Summary....220
10 Developing a testing strategy....222
10.1 Common test types and levels....223
10.1.1 Criteria for judging a test....224
10.1.2 Unit tests and component tests....224
10.1.3 Integration tests....225
10.1.4 API tests....226
10.1.5 E2EUI isolated tests....226
10.1.6 E2EUI system tests....227
10.2 Test-level antipatterns....227
10.2.1 The end-to-end-only antipattern....227
10.2.2 The low-level-only test antipattern....230
10.2.3 Disconnected low-level and high-level tests....232
10.3 Test recipes as a strategy....233
10.3.1 How to write a test recipe....233
10.3.2 When do I write and use a test recipe?....235
10.3.3 Rules for a test recipe....235
10.4 Managing delivery pipelines....236
10.4.1 Delivery vs. discovery pipelines....236
10.4.2 Test layer parallelization....237
Summary....239
11 Integrating unit testing into the organization....241
11.1 Steps to becoming an agent of change....241
11.1.1 Be prepared for the tough questions....242
11.1.2 Convince insiders: Champions and blockers....242
11.1.3 Identify possible starting points....243
11.2 Ways to succeed....244
11.2.1 Guerrilla implementation (bottom-up)....245
11.2.2 Convincing management (top-down)....245
11.2.3 Experiments as door openers....245
11.2.4 Get an outside champion....246
11.2.5 Make progress visible....247
11.2.6 Aim for specific goals, metrics, and KPIs....248
11.2.7 Realize that there will be hurdles....250
11.3 Ways to fail....250
11.3.1 Lack of a driving force....251
11.3.2 Lack of political support....251
11.3.3 Ad hoc implementations and first impressions....251
11.3.4 Lack of team support....252
11.4 Influence factors....252
11.5 Tough questions and answers....254
11.5.1 How much time will unit testing add to the current process?....254
11.5.2 Will my QA job be at risk because of unit testing?....255
11.5.3 Is there proof that unit testing helps?....256
11.5.4 Why is the QA department still finding bugs?....256
11.5.5 We have lots of code without tests: Where do we start?....256
11.5.6 What if we develop a combination of software and hardware?....257
11.5.7 How can we know we dont have bugs in our tests?....257
11.5.8 Why do I need tests if my debugger shows that my code works?....257
11.5.9 What about TDD?....257
Summary....257
12 Working with legacy code....259
12.1 Where do you start adding tests?....260
12.2 Choosing a selection strategy....262
12.2.1 Pros and cons of the easy-first strategy....262
12.2.2 Pros and cons of the hard-first strategy....262
12.3 Writing integration tests before refactoring....263
12.3.1 Read Michael Feathers book on legacy code....264
12.3.2 Use CodeScene to investigate your production code....264
Summary....264
appendixMonkey-patching functions and modules....266
A.1 An obligatory warning....266
A.2 Monkey-patching functions, globals, and possible issues....267
A.2.1 Monkey-patching a function the Jest way....269
A.2.2 Jest spies....269
A.2.3 spyOn with mockImplementation()....269
A.3 Ignoring a whole module with Jest is simple....270
A.4 Faking module behavior in each test....271
A.4.1 Stubbing a module with vanilla require.cache....272
A.4.2 Stubbing custom module data with Jest is complicated....274
A.4.3 Avoid Jests manual mocks....275
A.4.4 Stubbing a module with Sinon.js....275
A.4.5 Stubbing a module with testdouble....276
index....279
Symbols....279
A....279
B....280
C....280
D....280
E....281
F....281
G....282
H....282
I....282
J....282
K....283
L....283
M....283
N....284
O....284
P....284
Q....285
R....285
S....285
T....286
U....287
V....288
W....288
X....288
Unit testing is more than just a collection of tools and practices—it’s a state of mind! This bestseller reveals the master’s secrets for delivering robust, maintainable, and trustworthy code.Thousands of developers have learned to hone their code quality under the tutelage of The Art of Unit Testing. This revised third edition updates an international bestseller to reflect modern development tools and practices, as well as to cover JavaScript.
Effective unit tests streamline your software development process and ensure you deliver consistent high-quality code every time. With practical examples in JavaScript and Node, this hands-on guide takes you from your very first unit tests all the way to comprehensive test suites, naming standards, and refactoring techniques. You’ll explore test patterns and organization, working with legacy code and even “untestable” code. The many tool-agnostic examples are presented in JavaScript and carefully designed so that they apply to code written in any language.
The art of unit testing is more than just learning the right collection of tools and practices. It’s about understanding what makes great tests tick, finding the right strategy for each unique situation, and knowing what to do when the testing process gets messy. This book delivers insights and advice that will transform the way you test your software.
The Art of Unit Testing, Third Edition shows you how to create readable and maintainable tests. It goes well beyond basic test creation into organization-wide test strategies, troubleshooting, working with legacy code, and “merciless” refactoring. You’ll love the practical examples and familiar scenarios that make testing come alive as you read. This third edition has been updated with techniques specific to object-oriented, functional, and modular coding styles. The examples use JavaScript.
Examples use JavaScript, TypeScript, and Node.js.