DEV Community

Cover image for How to spy on a third party ES6 export in Vitest
Erik Pukinskis
Erik Pukinskis

Posted on

2

How to spy on a third party ES6 export in Vitest

This was a bit of a tricky one, and the solution from the Vitest docs didn't work for me.

Those docs suggested that I could do something like:

import * as Firestore from "firebase/firestore"
import { vi } from "vitest"

const onSnapshot = vi.spyOn(Firestore, "onSnapshot")
...
expect(onSnapshot).toHaveBeenCalled()
Enter fullscreen mode Exit fullscreen mode

but that didn't work, Vitest complains that it can't redefine that propery:

TypeError: Cannot redefine property: onSnapshot
 ❯ lib/useDoc.test.ts:12:4
     10| import type { Repo } from "~/test/helpers/factory"
     11| 
     12| vi.spyOn(Firestore, "onSnapshot")
Enter fullscreen mode Exit fullscreen mode

The solution

The answer wasn't too bad, but there are actually three steps:

1) Mock the import
2) Import the module in your test
3) Create the spy

Let's walk through each of those.

1) Mock the import

At the top level of my test file you need to mock the import using vi.mock. In the factory you feed to vi.mock you will replace the function you want to spy on with with a vi.fn:

type FakeFirestore = { onSnapshot(this: void): void }

vi.mock("firebase/firestore", async (getModule) => {
  const original: FakeFirestore = await getModule()

  return {
    ...original,
    onSnapshot: vi.fn().mockImplementation(original.onSnapshot),
  }
})
Enter fullscreen mode Exit fullscreen mode

Note that very useful importOriginal function which lets me keep the Firestore module fully functional. This is because vi.mock is really persnickety about referencing outside variables.

That factory function needs to be 100% self contained. Therefore you have to...

2) Import the module in your test

In order to have something to spy on, you'll need to import that same module directly in your test. This should be the mock object that you returned in your factory above, with the vi.fn where you stuck it. That'll give us something to spy on...

import * as Firestore from "firebase/firestore"
Enter fullscreen mode Exit fullscreen mode

3) Create the spy

Finally, we can create the spy, and place our expectations on it:

import { test, vi } from "vitest"

test("calls onSnapshot", () => {
  const onSnapshot = vi.spyOn(Firestore, "onSnapshot")

  /* trigger the onSnapshot call here in my code */

  expect(onSnapshot).toHaveBeenCalled()
})
Enter fullscreen mode Exit fullscreen mode

And that's it! My test passes.

Check out the complete source code here.

Follow me on Twitter if you want to see more Vite, Vitest, and Firestore tips!

Image description

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay