Who needs mocks?
The term “mock” or “mocking” in software testing is ubiquitous, but can have multiple meaning leading it to be confusing. Mocking can refer to functions, modules, servers… and what does it even mean? Do you need it?? Argh!?
Relax, mocks aren’t that hard. “Mocks” are a type of test pattern that can be useful when testing web applications. At a high level, mocking is the idea of replacing a function or part of a function with something that we explicitly control. A common example of this is a remote API call. Imagine the situation: you have a test that calls a remote API and tests whether the data is correctly rendered to the page. This might be a core part of your application, so needs to be tested, but simply writing a test that calls the API and tests whether the data is rendered to the page can be problematic. What happens if the API goes down? This will break your test, but this isn’t smart, as your code isn’t broken, it’s the external API that is broken. And what happens if you lose internet connection? This would also cause your test to fail. Or what if the API changes the format of the data it sends back? Again the test fail, but this failure is not a sensible representation of your software. Your code isn’t broken, it’s the external API that has changed. This concept is called a test’s “brittleness” - essentially how likely the test is the fail even if your code isn’t wrong.
What is mocking?
Mocks help get around this problem by reducing a test’s brittleness when calling APIs. Mocks fall under the category of “test doubles” as defined by Martin Fowler. Creating a mock function allows us to replace slow functions or API calls with something else, and gives us access to extra ways to test our code, such as capturing (and being able to assert against) how the function was called, how many times it was called, or what parameters the function was called with.
Mocking in Jest
Let's play with mocking in the popular JavaScript testing framework Jest. There are 3 main ways to create mock functions in Jest. These are
jest.fn()
jest.mock()
jest.spyOn()
Jest.fn() is used to mock a single function, while jest.mock() is used to mock a whole module. jest.spyOn() is slightly different in that it captures more information about how the function was called. All three are related and can be useful in their own ways.
Let’s look at an example. Let’s say we have a function that makes a really slow api call, and then gives us a response based on what is returned:
export async function getStockValue() {
const conversionRate = 0.91;
const data = await fetch(“http://www.slow-url.com”); // For this example imagine this returns a falsy value
if (!data || Object.keys(data).length < 1) {
return false;
} else {
return data.payload * conversionRate;
}
}
This function is obviously quite simple, but allows us to demonstrate the value in mocking. Let’s start with how we could write a test that isn’t very effective:
describe("the getStockValue function", () => {
it("returns false if no data is returned by the API", async () => {
const value = await getStockValue();
expect(value).toBe(false);
});
});
(If you don’t understand this test, check out the Jest docs). This simple test will test the functionality, but it is susceptible to all of the problems outlined previously - it will likely be slow and it will break if the internet connection or API itself is down. We can solve this while still testing the function with something like this:
describe("the getStockValue function", () => {
it("returns false if no data is returned by the API", async () => {
global.fetch = jest.fn(() => {
Promise.resolve();
});
const value = await getStockValue();
expect(fetch).toHaveBeenCalledTimes(1);
expect(value).toBe(false);
});
});
Here we mock the global fetch function, and dictate that it resolves a promise but doesn’t return anything. This will trigger our function to return false, which we can assert against, thus testing that the function returns false if the API doesn’t return anything. However, here you can see that mocking has also given us access to other ways to check our code is working as we expect it to: in this case being able to assert against the fetch having only been called one time.
That’s all, folks!
I hope this short article helped you with using Jest’s mocking capabilities. If you have any questions or want to reach out to me, drop me a line on murray@loupetestware.com. Best of luck in your mocking efforts!