Unit Test Tips #
Reset all global variables for each unit test case #
What environment the unit test cases are running on: Browser or Nodejs? #
- Because Nodejs does not have browser APIs
- Using
Karma
to run browser’s unit test- Using JS-DOM but it’s missing a lot of browser APIs
Work only when running alone #
Scenario:
A unit test case only pass when running alone but fail when running with other test cases
Check:
- Restore all mocks after mocking things:
sandbox.restore()
,jest.restoreAllMocks()
,vi.restoreAllMocks()
andvi.unstubAllGlobals()
atafterEach
- Reset global variables inner module: create a reset function to reset all variable of module to the initial value
Example:
// calculateThings.js
import cal1Thing from "./private/cal1Thing.js";
let cached = ""; // Global variable
export default (things) => {
if (cached) return cached;
let result = [];
for (let i = 0; i < things.length; i++) {
const calculatedThing = cal1Thing(things[i]);
result.push(calculatedThing);
}
cached = result;
return cached;
};
/* start-test-code */
export const testingOnly = {
resetCached: () => {
cached = "";
},
};
/* end-test-code */
// calculateThings.test.js
import calculateThings from "./calculateThings";
import cal1Thing from "./cal1Thing";
import { testingOnly } from "./calculateThings";
vi.mock("./cal1Thing");
describe("calculateThings", () => {
const { resetCached } = testingOnly;
afterEach(() => {
vi.restoreAllMocks();
});
it("should work as expected", () => {
cal1Thing.mockReturnValue("a string");
const caledThings = calculateThings([1, 2, 3]);
expect(caledThings).toEqual(["a string", "a string", "a string"]);
});
it("should return empty when empty cached and input is empty array", () => {
resetCached(); // remember reset cached
const caledThings = calculateThings([]);
expect(caledThings).toEqual("");
});
});
Setup code for testing only #
This setup will help you export function only when run test, not appear when build
Gulp - Rollup #
// rollup.bundle.js
import stripCode from "rollup-plugin-strip-code";
import {rollup} from rollup;
const stripcode = stripCode({
start_comment: "start-test-code",
end_comment: "end-test-code",
});
export default async () => {
const bundle = await rollup({input: 'mainFilePath.js', plugins: [stripcode]});
await bundle.write({
file: 'dist/destinationName.js',
format: 'iife',
name: 'YourObjectName',
sourcemap: false
})
}
// gulpfile.js
import rollupBundle from "./rollup.bundle.js";
const clean = () => {
// remove all previous build files or ST like that
},
lint = () => {
// run eslint warning
};
export default () => {
series(clean, rollupBundle, lint);
};
Vite - Vitest #
Mock module #
When you mock a module, everything you exported in this module will be mocked and can not act like original (even if you call vi.restoreAllMocks()
)
Solution:
If your module exports alots, and you only want to mock one thing, you should split it into another module
Example:
// calculateThings.js
import cal1Thing from "./private/cal1Thing.js";
export default (things) => {
let result = [];
for (let i = 0; i < things.length; i++) {
const calculatedThing = cal1Thing(things[i]);
result.push(calculatedThing);
}
return result;
};
// calculateThings.test.js
import calculateThings from "./calculateThings";
import cal1Thing from "./cal1Thing";
vi.mock("./cal1Thing");
describe("calculateThings", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("should work as expected", () => {
cal1Thing.mockReturnValue("a string");
const caledThings = calculateThings([1, 2, 3]);
expect(caledThings).toEqual(["a string", "a string", "a string"]);
});
});
Mock library #
// authUtils.js
import jwt from "jsonwebtoken";
export const createTokenPair = (payload, privateKey) => {
try {
const accessToken = jwt.sign(payload, privateKey, {
algorithm: "RS256",
expiresIn: "2 days",
});
const refreshToken = jwt.sign(payload, privateKey, {
algorithm: "RS256",
expiresIn: "7 days",
});
return { accessToken, refreshToken };
} catch (error) {
throw new Error("createTokenPair got error");
}
};
// authUtils.test.js
import { describe, expect, it } from "vitest";
import { createTokenPair, genKeyPairRSA } from "../../src/auth/authUtils";
import jwt from "jsonwebtoken";
describe("auth/authUtils.js", () => {
describe("createTokenPair", () => {
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it("should throw error when jwt lib got error", () => {
vi.spyOn(jwt, "sign").mockImplementation(() => {
throw new Error("JWT error");
});
const payload = {
userId: 123,
email: "a@b.co",
};
const { privateKey, publicKey } = genKeyPairRSA();
const toBeThrowError = () => {
const { accessToken, refreshToken } = createTokenPair(
payload,
privateKey
);
};
expect(toBeThrowError).toThrowError("createTokenPair got error");
});
});
});
Sinon #
Stub a function that is called by another function in the same module #
Using this.[func_name]
when calling it in your module
Stub an export default function #
import * as query from "/database/query";
const makeQueryStub = sandbox.stub(query, "default").resolves([]);
Mocha - Chai - Sinon sample #
import sinon from "sinon";
import { function_name, callback_function_name } from "../module_name.js";
const sandbox = sinon.createSandbox();
describe("module_name", function () {
afterEach(function () {
sandbox.restore();
});
describe("function_name", function () {
it("Should be a function", function () {
expect(function_name).to.be.a("function");
});
it("Should return this value if window.screen is undefined", function () {
sandbox.stub(window, "screen").value(undefined);
expect(function_name()).equal("expected string");
});
it("should return expected object when running callback function", function (done) {
callback_function_name(function (returnedData) {
expect(returnedData).to.deep.equal({ name: "expected object" });
done();
});
});
});
});
Jest - Sinon sample #
import sinon from "sinon";
import { function_name, async_function_name } from "../module_name.js";
const sandbox = sinon.createSandbox();
describe("module_name", function () {
afterEach(function () {
sandbox.restore();
});
describe("function_name", function () {
it("Should be a function", function () {
expect(typeof function_name).toEqual("function");
});
it("Should return this value if window.screen is undefined", function () {
sandbox.stub(window, "screen").value(undefined);
expect(function_name()).toEqual("expected string");
});
it("should return expected object when handling function asynchronously", async () => {
const returnedData = await async_function_name();
expect(returnedData).toEqual({ name: "expected object" });
});
});
});
Help improve my blog
Was this page helpful to you?
This page was last modified at 2024-01-06