TIL: How to Mock Classes Using Vitest

Recently I have been creating a SvelteKit app, when creating a new SvelteKit app you get a choice of different things you can add. Such as using vitest for unit testing.

I needed to spy on/mock a method in a class, to see if it was called when a button was pressed and, it was called with the correct arguments.

Let’s say we have a Button component which looks like this:

<!-- button.svelte -->
<script lang="ts">
    import { API } from "./api"

    const api = new API()
</script>

<button on:click={api.create}>Press Me!</button>

Then the api.ts looks something like this:

export class API {
	async create() {
        // ... does something
	}
}

So how do we test that the create method is called? Let’s assume we will be using the svelte testing library 1.

// __tests__/button.test.ts
import { render } from "@testing-library/svelte";
import userEvent from "@testing-library/user-event";
import { describe, expect, test, vi } from "vitest";

import Button from "../Button.svelte";
import { API } from "./api";

describe("Button", () => {
	test("Successfully render Button", async () => {
		const user = userEvent.setup();
		const mock = vi.spyOn(API.prototype, "create");

		const { getByRole, getByLabelText } = render(Button, { props: {} });

		const button = getByRole("button", { name: "Add Bookmark" });
		await user.click(button);
		expect(mock).toHaveBeenCalled();
	});
});

The key being this line vi.spyOn(API.prototype, "create"), where we need to use .prototype. So we can access the methods in the class. Without the .prototype we will not find the method on the object itself.

That’s it! Thanks for reading.

Appendix