@@ -85,14 +85,14 @@ and at the higher level of "does it all hang together?".
85
85
Our tests need to be fast enough to write,
86
86
but more importantly, fast to run.
87
87
We want to get into a smooth, productive workflow,
88
- and try to enter into that holy credo of programmers, the "flow state".
88
+ and that holy credo of programmers, the "flow state".
89
89
Beyond that, we want our tests to take some of the stress out of programming,
90
90
encouraging us to work in small increments,
91
91
with frequent bursts of dopamine from seeing green tests.
92
92
93
93
==== Driving Better Design
94
94
95
- Finally our tests should help us to write better code.
95
+ And our tests should help us to write _better_ code.
96
96
Firstly, by enabling fearless refactoring.
97
97
Secondly, by giving us feedback on the design of our code.
98
98
Writing the tests first lets us think about our API from the outside-in,
@@ -103,9 +103,7 @@ As we'll see, designing code to be more testable
103
103
often leads to code that has clearly identified dependencies,
104
104
and is more modular and more decoupled.
105
105
106
-
107
-
108
- As we continuously think about what kinds of tests to write
106
+ As we continuously think about what kinds of tests to write,
109
107
we are trying to achieve the optimum balance of these different desiderata.
110
108
111
109
@@ -143,10 +141,10 @@ But people will often tell you that a "true" unit test should be more isolated.
143
141
It's meant to test a single "unit" of software,
144
142
and your database "should" be outside of that.
145
143
Why do they say that?
146
- Apart from the lovely warm glow of smug that they get from should-ing us?
144
+ Apart from the smug from should-ing us?
147
145
148
146
As you can tell,
149
- I think the argument from definitions is a bit of a red herring.
147
+ I think the argument from _definitions_ is a bit of a red herring.
150
148
But you might hear instead, "the database is hot lava!"
151
149
as Casey Kinsey put it in a memorable DjangoCon talk.
152
150
There is real feeling and real experience behind these comments.
@@ -164,17 +162,19 @@ At PythonAnywhere, our functional test suite didn't just rely on the database,
164
162
it would spin up a full test cluster of 6 virtual machines.
165
163
A full run used to take at least 12 hours,
166
164
and we'd have to wait overnight for our results.
165
+ That was one of the least productive parts of an otherwise extraordinary workflow.
167
166
168
167
At Kraken, the full test suite does only take about 45 minutes,
168
+ which is not bad for nearly 10 million lines of code,
169
169
but that's only thanks to a quite frankly ridiculous level of parallelisation
170
170
and associated expenditure on CI.
171
171
We're now spending a lot of effort on trying to move more of our unit
172
172
tests to being "true" unit tests.
173
173
174
174
The problem is that these things don't scale linearly.
175
175
The more database tables you have,
176
- the more foreign keys between them,
177
- and the relationships can start to increase geometrically.
176
+ the more relationships between them,
177
+ and that starts to increase geometrically.
178
178
179
179
So you can see why, over time, these kinds of tests
180
180
are going to fail to meet our desiderata because they're too slow
@@ -334,7 +334,6 @@ by Dave Farley.
334
334
Well that's all very well Harry, you might say,
335
335
but our current test setup is nothing like this!
336
336
How do we get there from _here_?
337
-
338
337
We've seen how to use mocks to isolate ourselves from external dependencies.
339
338
Are they the solution then?
340
339
@@ -363,15 +362,14 @@ NOTE: I'm glossing over the use of mocks in a "London-school"
363
362
=== The Actual Solutions Are Architectural
364
363
365
364
The actual solution to the problem isn't obvious from where we're standing,
366
- but it actually lies in rethinking the architecture of our application.
365
+ but it lies in rethinking the architecture of our application.
367
366
In brief, if we can _decouple_ the core business logic of our application
368
367
from its dependencies, then we can write true unit tests for it,
369
368
that do not depend on those, um, dependencies.
370
369
371
370
Integration tests are most necessary at the _boundaries_ of a system--at
372
371
the points where our code integrates with external systems,
373
372
like the database, filesystem, network, or a UI.
374
-
375
373
Similarly, it's at the boundaries that the downsides of test isolation and
376
374
mocks are at their worst, because it's at the boundaries that you're most
377
375
likely to be annoyed if your tests are tightly coupled to an implementation,
@@ -460,7 +458,7 @@ Martin Fowler and Kent Beck,
460
458
which you can follow https://martinfowler.com/articles/is-tdd-dead/[here].
461
459
462
460
Like any technique, these patterns can be misused,
463
- but I wanted to make the case for the upside of these patterns :
461
+ but I wanted to make the case for their upside:
464
462
by making our software more testable,
465
463
we also make it more modular and maintainable.
466
464
We are forced to clearly separate our concerns,
@@ -501,7 +499,6 @@ image::images/frog-in-a-pan-placeholder.png["An illustration of a frog being slo
501
499
502
500
For small to medium-sized applications, as we've seen, the Django test runner
503
501
and the integration tests it encourages us to write are just fine.
504
-
505
502
The problem is knowing when it's time to make the change
506
503
to a more decoupled architecture, and start striving explicitly for the Test Pyramid.
507
504
@@ -510,29 +507,26 @@ since I've only experienced environments where either someone else made the deci
510
507
before I joined, or the company is already struggling with a point where it's
511
508
(at least arguably) too late.
512
509
513
- Another is, the longer you leave it, the harder it is.
514
-
515
- Yet another is, because the pain is only going to set in gradually,
510
+ One thing to bear in mind, though, is that the longer you leave it, the harder it is.
511
+ Another is that because the pain is only going to set in gradually,
516
512
like the apocryphal boiled frogs, you're unlikely to notice
517
513
until you're past the "perfect" moment to switch.
518
-
519
- One more suspicion: it's never going to be a convenient _time_ to switch.
514
+ And on top of that, it's _never_ going to be a convenient time to switch.
520
515
This is one of those things, like tech debt,
521
516
that are always going to struggle to justify themselves in the face of more
522
517
immediate priorities.
523
518
524
519
So perhaps one strategy would be an Odysseus pact,
525
520
tie yourself to the mast, and make a commitment--while the tests are still fast--to
526
521
set a "red line" for when to switch.
527
- "if the tests ever take more than 10 seconds to run locally,
522
+ For example: "if the tests ever take more than 10 seconds to run locally,
528
523
then it's time to rethink the architecture".
529
524
530
525
531
526
I'm not saying 10 seconds is the right number by the way.
532
527
I know plenty of people who are perfectly happy to wait 30 seconds.
533
528
And I know Gary Bernhardt, for one, would get very nervous
534
529
at a test suite that takes more than 100ms.
535
-
536
530
But I think the idea of drawing that line in the sand, wherever it is,
537
531
_before_ you get there, might be a good way to fight the "boiled frog" problem.
538
532
@@ -551,16 +545,18 @@ and spot the problems before they get too large.
551
545
In this book, I've been able to show you how to use TDD,
552
546
and talk a bit about why we do it, and what makes a good test,
553
547
but we're inevitably limited by the scope of the project.
554
-
555
548
What that's meant is that some of the more advanced uses of TDD,
556
549
particularly the interplay between testing and architecture,
557
550
have been beyond the scope of this book.
558
- But I hope that this chapter has given you enough of a guide to find your way
551
+
552
+ But I hope that this chapter has been a bit a guide to find your way
559
553
around that topic as your career progresses.
560
554
561
555
562
556
==== Further Reading
563
557
558
+ A few places to go for more inspiration:
559
+
564
560
Fast Test, Slow Test and Boundaries::
565
561
Gary Bernhardt's talks from Pycon
566
562
https://www.youtube.com/watch?v=RAxiiRPHS9k[2012] and
@@ -613,3 +609,6 @@ A Take From the World of Functional Programming::
613
609
explores the idea of "Functional Core, Imperative Shell".
614
610
Don't worry, you don't need a crazy FP language like Haskell or Clojure to understand it,
615
611
it's written in perfectly sensible JavaScript.
612
+
613
+
614
+ Happy testing!
0 commit comments