관리 메뉴

Dev Blog

Section7: Mocking & Spies: Dealing with Side Effects 본문

Online Lectures/JavaScript Unit Testing - The Practical

Section7: Mocking & Spies: Dealing with Side Effects

Nomad Kim 2023. 4. 5. 06:40

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.

Comments