Section7: Mocking & Spies: Dealing with Side Effects
Dealing with Side Effects
- Dealing with Side Effects & External Dependencies
- Understanding Spies & Mocks
- Examples
Why need?
- In some cases, test interacts with upper system such as hard drive, database outside of program and code.
- Means that there could be side effects.
- e.g. important files or data could be deleted or edited accidentally.
spy
fn creates an empty function that keeps track of any function executions of that function. Means calls to the function. It also keeps track of arguments that are provided with those calls.
const logger = vi.fn();
return expect(writeData(testData, testFileName)).resolves.toBeUndefined();
This code makes error. TypeError: Cannot read properties of undefined (reading 'then') Because spy doesn't have methods. In this case, use fs.writeFile that is run in writeData function.
import { it, expect, vi } from "vitest";
import { promises as fs } from "fs";
import writeData from "./io";
it("should execute the writeFile method", () => {
const testData = "Test";
const testFileName = "test.txt";
writeData(testData, testFileName);
expect(fs.writeFile).toBeCalled();
});
mock
In case that there are not unusable modules we don't own, we cannot use spy. In this example case, it doesn't receive writeFile as an argument.
Mock works with built in or third-party module and own modules, own files.
- This starts Vitest's or Jest's auto-mocking algorithm finding module by name or path and replace all the functions with empty spy functions.
- And importantly, vi.mock is hoisted automatically to the top for mocking away before importing.
vi.mock("fs");
/**
* @description By setting the second argument, able to return wanted outcome
*/
vi.mock("path", () => {
return {
default: {
join: (...args) => {
/**
* @description last argument is filename. In this case, testFileName.
* const storagePath = path.join(process.cwd(), "data", filename);
* */
return args[args.length - 1];
},
},
};
});
it("should execute the writeFile method", () => {
const testData = "Test";
const testFileName = "test.txt";
writeData(testData, testFileName);
expect(fs.writeFile).toBeCalledWith(testFileName, testData);
});
For multiple test, can set global mock setting(__mocks__).
Then, codes using resolves method will works because it looks for ‘__mocks__’ folder and promises function for using returned Promise of writeFile method.