The Art of Unit Testing, 2nd Edition

Roy Osherove

June 15, 2022 Chapter 1. The basics of unit testing

This idea of a unit of work means, to me, that a unit can span as little as a single method and up to multiple classes and functions to achieve its purpose.

June 15, 2022 Chapter 1. The basics of unit testing

Integration testing is testing a unit of work without having full control over all of it and using one or more of its real dependencies, such as time, network, database, threads, random number generators, and so on. To summarize: an integration test uses real dependencies; unit tests isolate the unit of work from its dependencies so that they’re easily consistent in their results and can easily control and simulate any aspect of the unit’s behavior.

June 30, 2022 Chapter 3. Using stubs to break dependencies

We call these classes fake because we don’t want to commit to them only being used as stubs or as mocks.

June 30, 2022 Chapter 3. Using stubs to break dependencies

The Extract and Override method is great for simulating inputs into the code under test, but if you’re also testing interactions between objects (the topic of the next chapter), be sure to have it return an interface rather than an arbitrary return value. It will make your testing life easier.

June 30, 2022 Chapter 3. Using stubs to break dependencies

TOOD can present interesting advantages over classic object-oriented design, such as allowing maintainability while still permitting tests to be written against the code base.

June 30, 2022 Chapter 4. Interaction testing using mock objects

interaction testing, which deals with the third kind of result: calling a third party. Value-based testing checks the value returned from a function. State-based testing is about checking for noticeable behavior changes in the system under test, after changing its state.

July 04, 2022 Chapter 4. Interaction testing using mock objects

The basic difference is that stubs can’t fail tests. Mocks can

July 09, 2022 Chapter 4. Interaction testing using mock objects

In a test where you test only one thing (which is how I recommend you write tests), there should be no more than one mock object. All other fake objects will act as stubs. Having more than one mock per test usually means you’re testing more than one thing, and this can lead to complicated or brittle tests

July 11, 2022 Chapter 5. Isolation (mocking) frameworks

It’s considered good practice to test only one concern per test. Testing more than one concern can lead to confusion and problems maintaining the test. Having two mocks in a test is the same as testing several end results of the same unit of work. If you can’t name your test because it does too many things, it’s time to separate it into more than one test.

July 11, 2022 Chapter 6. Digging deeper into isolation frameworks

Constrained frameworks usually work by generating code at runtime that inherits and overrides interfaces or base classes, just as you did in the previous chapter, only you did it before running the code. That means that these isolation frameworks also have the same requirements for compiling: the code you want to fake has to be public and inheritable (nonsealed), has to have a public constructor, or should be an interface. For base classes, methods you’d like to override need to be virtual

July 12, 2022 Chapter 6. Digging deeper into isolation frameworks

Wide faking is the ability to fake multiple methods at once

July 12, 2022 Chapter 6. Digging deeper into isolation frameworks

You have to know how many mocks and stubs there are in a test, because more than a single mock in a test is usually a problem. When it doesn’t distinguish between the two, the framework could tell you that something is a mock when in fact it’s used as a stub. It takes you longer to understand whether this is a real problem or not, so the test readability is hurt.

July 12, 2022 Chapter 6. Digging deeper into isolation frameworks

In .NET, unconstrained frameworks use the profiling APIs, whereas most constrained frameworks generate and compile code at runtime, just as you do manually with handwritten mocks and stubs.

July 16, 2022 Chapter 7. Test hierarchies and organization

The template test class pattern is an abstract class that contains abstract test methods that derived classes must implement. The driving force behind this pattern is the need to be able to dictate to deriving classes which tests they should always implement.

July 17, 2022 Chapter 8. The pillars of good unit tests

test that contains logic is usually testing more than one thing at a time, which isn’t recommended, because the test is less readable and more fragile. But test logic also adds complexity that may contain a hidden bug. A unit test should, as a general rule, be a series of method calls with assert calls, but no control flows, not even try-catch, and with assert calls.

July 17, 2022 Chapter 8. The pillars of good unit tests

Because you already know how the end result should look, nothing stops you from using it in a hardcoded way. Now you don’t care how the end result was accomplished, but you find out if it didn’t pass. And you have no logic in your test that might have a bug.

July 17, 2022 Chapter 8. The pillars of good unit tests

if your unit test asserts on more than a single object, it may be testing more than one concern. Or if it tests both that the same object returns the right value and that the system state changes so that the object now behaves differently, it’s likely testing more than one concern.

July 17, 2022 Chapter 8. The pillars of good unit tests

Setup methods should only contain code that applies to all the tests in the current test class, or the method will be harder to read and understand

July 17, 2022 Chapter 8. The pillars of good unit tests

Developers abuse setup methods in several ways: Initializing objects in the setup method that are used in only some tests in the class Having setup code that’s lengthy and hard to understand Setting up mocks and fake objects within the setup method

July 17, 2022 Chapter 8. The pillars of good unit tests

My preference is to have each test create its own mocks and stubs by calling helper methods within the test, so that the reader of the test knows exactly what’s going on, without needing to jump from test to setup to understand the full picture.

July 18, 2022 Chapter 8. The pillars of good unit tests

Instead of adding multiple asserts, you can create a full object to compare against, set all the properties that should be on that object, and compare the result and the expected object in one assert. The advantage of this approach is that it’s much easier to understand what you’re testing and to recognize that this is one logical block that should be passing, not many separate tests

July 25, 2022 Chapter 11. Design and testability

These are the FICC properties: fast, isolated, configuration-free, and consistent. If it’s hard to write such a test, or if it takes a long time to write it, the system isn’t testable.

July 25, 2022 Chapter 11. Design and testability

I find lots of badly designed, very testable code out there. Proof positive that TDD, without proper design knowledge, is not necessarily a good influence on design