Network
Loading "Intro to Network"
Run locally for transcripts
Network interactions are the most common reason to introduce mocking to your test suite, as nearly every application depends on them. You build products so they do something, and on the web it takes the client and the server to achieve that. It’s no surprise that many developers, when thinking of mocking, specifically imagine API mocking.
Why you should mock the network
The reasons for mocking network requests are similar to mocking anything else: to eliminate the network’s impact on test results.
But why? Your application makes those requests in production, shouldn't you keep the behavior the same in tests?
Yes and no. To help me explain this, I will reach again for the 📜 Golden Rule of Assertions:
A test must fail if, and only if, the intention behind the system is not met.
This means that factors affecting test results should align with the test’s purpose. Making the request itself is never the purpose; the network is simply a protocol to do something. The real focus is the logic surrounding the request.
For example, you may be fetching the user object to render a personalized message in the UI:
async function Greet({ id }: { id: string }) {
// Beware: pseudo-code ahead!
const response = await fetch(`/users/${id}`)
const user = await response.json()
return <p>Hello, {user.firstName}!</p>
}
Indeed, making the
GET /users/:id
request is a part of the implementation, but that's not the purpose of the Greet
component. The purpose is to render the received data. Often, it is also to handle request or response errors gracefully so they don't disrupt the user experience.But think about what happens if you leave that request as-is in tests. First, it's going to take time to communicate with the server. But a far more important thing is that the server response is directly responsible for the test result.
You aren't testing the server though. When talking about integration level testing, the server has nothing to do with your
Greet
component—it's simply another variable part of the testing equation, much like date and time!You can use API mocking to control the network, allowing you to respond to the same request in different ways to test edge cases and error handling.
How does API mocking work?
Whether you are using a third-party request client, like
got
or axios
, or relying on plain fetch
, mocking a network request often comes down to patching the global APIs responsible for it. In the browser, those are fetch
and XMLHttpRequest
, and in Node.js it may be http.ClientRequest
or net.Socket
(or the same global fetch
in modern versions of Node.js). In that regard, mocking the network is not much different than mocking globals, which you already know how to do.That also means you already know about the dangers of patching globals too. Those dangers manifest greatly in network mocking because it often throws away the network code. While you don't want the actual request to happen during tests, the "later" you establish the interception, the better. You wouldn't want GET requests with a body to be treated with mock responses, would you?
If you would like to dive deeper into the request interception in JavaScript, I have written a series of articles on API mocking. The best place to start is by reading 📜 The Request Journey.
Mocking introduces deviations, so you naturally want to minimize how much your tested code diverges from its behavior in production. There are multiple API mocking solutions out there, and I encourage you to explore them all, but some tend to diverge your code more than others.
That is why in this workshop we will be using Mock Service Worker. It builds on top of the web standards, like the Service Worker and the Fetch API, and provides one of the least intrusive interception algorithms in JavaScript, allowing your tests to run as much of the actual network code as possible.
It's time for you to see it in action!