The document discusses principles for writing good tests, including clarity, completeness, conciseness, resilience, and test behaviors rather than methods. Tests should be readable documentation, contain all needed information concisely, and not require changing unless the code behavior changes. Individual tests should focus on single behaviors so that adding new behaviors only requires new tests. Test names should describe the scenario and expected outcome to make purposes and failures clear.
6. A test is complete when its body contains
all of the information you need to
understand it, and concise when it doesn't
contain any other distracting information.
10. Once written, a resilient test
doesn't have to change unless the
purpose or behavior of the class
being tested changes.
Adding new behavior should only
require adding new tests, not
changing old ones. The original test
above isn't resilient since you'll have
to update it (and probably dozens of
other tests!) whenever you add a new
irrelevant constructor parameter.
Moving these details into the helper
method solved this problem.
12. bad test that verifies an entire method
Displaying the name of the purchased
item and sending an email about the
balance being low are two separate
behaviors, but this test looks at both
of those behaviors together just
because they happen to be triggered
by the same method. Tests like this
very often become massive and
difficult to maintain over time as
additional behaviors keep getting
added in—eventually it will be very
hard to tell which parts of the input
are responsible for which assertions.
The fact that the test's name is a
direct mirror of the method's name is
a bad sign.
13. use separate tests to verify separate behaviors
Now, when someone adds a new
behavior, they will write a new test for
that behavior. Each test will remain
focused and easy to understand, no
matter how many behaviors are
added. This will make your tests more
resilient since adding new behaviors
is unlikely to break the existing tests,
and clearer since each test contains
code to exercise only one behavior.
17. Make sure test names contain
both the scenario being tested
and the expected outcome
18. Benefits
• If you want to know all the possible behaviors a class has,
all you need to do is read through the test names in its
test class
• By giving tests more explicit names, it forces you to split
up testing different behaviors into separate tests
• You can easily tell if some functionality isn’t being tested
• When a test fails, you can immediately see what
functionality is broken without looking at the test’s source
code
19. Benefits
• If you want to know all the possible behaviors a class has,
all you need to do is read through the test names in its
test class
• By giving tests more explicit names, it forces you to split
up testing different behaviors into separate tests
• You can easily tell if some functionality isn’t being tested
• When a test fails, you can immediately see what
functionality is broken without looking at the test’s source
code
20. Benefits
• If you want to know all the possible behaviors a class has,
all you need to do is read through the test names in its
test class
• By giving tests more explicit names, it forces you to split
up testing different behaviors into separate tests
• You can easily tell if some functionality isn’t being tested
• When a test fails, you can immediately see what
functionality is broken without looking at the test’s source
code
21. Benefits
• If you want to know all the possible behaviors a class has,
all you need to do is read through the test names in its
test class
• By giving tests more explicit names, it forces you to split
up testing different behaviors into separate tests
• You can easily tell if some functionality isn’t being tested
• When a test fails, you can immediately see what
functionality is broken without looking at the test’s source
code
22. Benefits
• If you want to know all the possible behaviors a class has,
all you need to do is read through the test names in its
test class
• By giving tests more explicit names, it forces you to split
up testing different behaviors into separate tests
• You can easily tell if some functionality isn’t being tested
• When a test fails, you can immediately see what
functionality is broken without looking at the test’s source
code