- [Presenter] Unit testing
requires isolation of the target code.
However, due to the power of (faintly speaking) Modularity,
pretty much, everything depends on everything.
How can we isolate our code,
so we only test the intended functionality?
There's a solution to this problem,
and it involves replacing
and inspecting code execution with test structures,
known as stubs, spies and mocks.
In this chapter,
we're going to start by discussing what Test Doubles are,
and how they can be used to replace code.
Then, we'll compare some techniques
for overriding dependencies
by manipulating the required cash.
With that foundation,
we'll apply these techniques to our testing.
We'll control the responses of methods,
observe interactions between components
and verify expected behavior of the code we're testing.
We'll do all of this,
without having to modify how our modules require code,
which is fantastic.
Let's start by discussing Test Doubles,
which are a special type of code.
What are they?
A Test Double is a replacement
for an arbitrary component of executable code,
like a module or a method.
A Test Double looks and behaves
in a similar way to what it replaces,
down to providing the same API.
However, the functionality
is typically a much simplified version of the original.
Test Doubles can have deterministic behavior
and do the same expected thing
each time it's interacted with,
such as always responding with a particular value or error.
Test Doubles are typically configured during the test setup,
rather than in the test spec itself.
- Why should we bother using a Test Double?
First of all, unit testing requires isolation
and a Test Double allows us replace code
in order to isolate the target.
Test Doubles, provide a consistent
and completely expected response every time.
Finally, Test Doubles are fast,
because they typically contain little to no executable code.
Gerard Meszaros as part of his book,
on "xUnit Test Patterns"
Wrote a fantastic definition of Test Doubles
and the variations.
They include, test stubs, test spies, mock objects,
fake objects, and dummy objects.
We'll look at each in turn along with use cases.
A test stub is a Test Double
that simulates the behavior of a component
by taking complete control over the response.
A stub won't respond to a request unless programmed to,
which is good for controlling execution.
A stab temporarily replaces a real component,
so the original can always be restored as needed.
In terms of usefulness,
a stub can be used to verify indirect input.
For example, you can fake a database query response
by specifying a hard-coded response.
You could also disable logging
by replacing the logger with an empty stub,
so calls to log just do nothing.
A test spy like its name implies,
observes interactions with tested code.
Primarily, spies are used to count the number of times
a component was called,
along with how it was called and what it responded with.
In this manner,
spies verify the indirect outputs of components.
A test spy can either be added to an existing method
in order to see how it behaves
or replace it completely like a stub.
For example, a spy can determine
if the validator was called once with transformed values.
Mock objects are interesting.
A mock verifies behavior, specifically the expectations
around how something is called.
This includes with what arguments and how many times.
If those expectations are not met,
then when verified, a mock will fail the test.
In its purest form, mocks do not return anything
which differentiates it from a stub.
You'll want to use a mock
when you want to verify the method
in which something was being used,
for example, you'd use a mock
when verifying that saving only calls the database once.
There're some definite similarities
between mock objects and test spies.
And the demonstrations later in this chapter
should help clarify the difference.
A fake object is a working implementation of a component
that takes a shortcut, typically, for performance reasons.
Fake objects are not usable in production,
and are intended only for testing purposes.
For example, you could replace a database implementation
with a simple in-memory array with the same API.
That would be faster,
but you'd want to use a real database in production.
The final variation of a Test Double is a dummy object,
which just provides a parameter for a tested method.
For example, a dummy object can be used
to test reservation validation
using arbitrary input that doesn't change.
We've already done this,
just without the label of dummy object.
Let's review and compare the different Test Doubles.
A test stub verifies indirect inputs
by providing a controlled response.
A test spy records indirect outputs
by reporting on how the request was made
for later verification.
A mock object verifies indirect outputs
by recording how it's used
in order for expectations to check
to see if it's used incorrectly.
A fake object just runs faster with less functionality,
and a dummy object specifies values used for testing.
If you're interested in learning more
about the philosophy and implementations,
check out the articles on xunitpatterns.com
and martinfowler.com.
They both go into greater detail
and provide thorough explanations of each concept.
Within the context of just everything is considered a mock,
a jest mock can be used to stub out functionality,
a jest mock can spy to keep track of usage.
Jest mocks provide expectations about how the code was used.
Jest mocks include fake timers
for manipulating time-based events
Dummy objects aren't included,
because you don't need a framework to use them.
So, why go through the trouble of defining each,
if jest conflates the terminology?
Each of these terms can be found
throughout jest's documentation,
and they are used in other frameworks as well.
With that in mind,
how can we use jest to mock a single function.