Chris James 15 Jul 2020 https://quii.dev
People say
We mock these dependencies in the test
And also you hear advice that
Too much mocking is bad
One of these statements is definitely true, the other not so much
To separate concerns, we often make objects that will “depend” on other objects.
We “inject” or “pass in” these dependencies when we construct them so they can do a job.
This is called Dependency Injection.
A ShoppingCart
sends an email when an order is sent.
We don’t want ShoppingCart
to have to know how to send emails as well as everything else so we give it an Emailer
when we create it.
new ShoppingCart(new MailgunEmailer())
It can then use the emailer without having to know how emails are sent, and you have your separation of concerns.
sendOrder() {
// interesting domain logic
this.emailer.send(confirmation)
}
With tests we prefer not to use real objects for dependencies because we want to be able to control them, so we use something different
Not specifically mocks
A test double is what you inject into an "object under test" (OUT) to control a dependency.
Like a stunt double!
Using the precise words for test doubles helps reveal intent and documents your design more effectively
Sometimes you have arguments to functions that you don’t care about. The arguments you pass in are called “dummies”
const dummyLogger = jest.fn()
Some objects will query dependencies for data.
Stub the dependency
const stubUserService = jest.fn(() => 'Miss Jones')
Sometimes you want to call a method (or more abstractly send a message) to a dependency.
It can be hard to tell in a test that this happened from the outside as it’s an internal detail.
If you want to verify these commands are sent, use a spy.
const spyEmailSender = jest.fn()
// do some stuff
expect(spyEmailSender.mock.calls.length).toBe(1)
Usually a “real” implementation but constrained.
Like an in-memory database.
Not as popular anymore due to things like Docker.
Mocks are a kind of test double!
Mocks are precise and encapsulate the detail of your interactions more explicitly.
You describe upfront what calls you expect on your test double
- Order
- Arguments
- Specific return values
If the object under test doesn’t call things as expected the mock will error.
The risk is your tests become needlessly precise and coupled to implementation detail.
There is also true of spies as they have quite a similar nature.
If you're overly specific with your mocks, when you want to refactor things you may find tests failing for annoying reasons that have nothing to do with behaviour
Listen to your tests if they cause you pain
If you have many test doubles in a test that means your OUT has many dependencies.
Does that sound right to you?
Correct naming things help you be precise when communicating with other developers.
Tests are supposed to act as documentation so describing your test doubles correctly will help reveal the design of the test for maintainers.
So start naming things correctly, today!