An advanced guide to Vitest testing and mocking
Tags: vitest, vue, webdev
Published: 2024-06-11 17:09:05

**Written by [Sebastian Weber
](https://blog.logrocket.com/author/sebastianweber/)✏️**

Testing is crucial in modern software development for ensuring code quality, reliability, and maintainability. However, the complexity of testing can often feel overwhelming. 

In this article, we will first delve into real-world use cases to demonstrate testing strategies using Vitest and its various APIs. We will also share a [cheat sheet with condensed Vitest examples](#vitest-cheat-sheet) that can serve as a useful resource for both aspiring testers and experienced developers. 

Let’s get started!

## An introduction to testing

If you are new to testing, it is worth familiarizing yourself with some of its important terms and concepts:

*   **Test doubles**: An umbrella term for the following concepts
*   **Dummies**: Objects or primitive values that are passed around and sometimes not even used. They are typically used as function arguments (e.g., a hard-coded customer object)
*   **Fakes**: Functions that have working implementations but take shortcuts that make them unsuitable for production, such as an in-memory database
*   **Stubs**: Functions that provide ready-made answers to the calls made during the test and generally do not respond to anything that has not been programmed for the test
*   **Spies**: Stubs that also record some information about how they are used, e.g., with which arguments a spied function is called
*   **Mocks**: Pre-set functions designed to anticipate and specify the calls they should receive, helping in the verification of expected behavior

Because there are no official definitions, there are differing opinions on the differences between spies and mocks. This article defines spies as tools that do not change the original implementation of a module, using real actors to verify expected interactions. In contrast, mocks are fully or partly replaced modules with simplified functions to control tests. In your tests, you don't want to make actual network calls, so you have to replace the module responsible for it with a mock. 

For a deeper dive into testing, check out this [guide to unit testing](https://blog.logrocket.com/product-management/unit-testing-guide/), [a guide to unit and integration testing for Node.js apps](https://blog.logrocket.com/unit-integration-testing-node-js-apps/), and a [Vitest tutorial for automated testing using Vue components](https://blog.logrocket.com/guide-vitest-automated-testing-vue-components/). 

Now, let’s explore in detail how to write tests with Vitest.

## A deep dive into testing with Vitest

The goal of this article is to teach you how to use Vitest's API to write robust, readable, and maintainable tests. If you want to learn more about Vitest architecture, I recommend [Ben Holmes's video](https://youtu.be/rBdGDiwVyes?si=rvNLfAaUHuA9fqO9&t=666), which shows that Vitest features built-in modern design decisions (e.g., supporting ESM modules). 

Before we delve into the code examples, let’s first set up a good test workflow.

### Setting up a testing workflow

Establishing a good testing workflow can greatly enhance productivity. By installing [Vitest’s official extension for VS Code](https://marketplace.visualstudio.com/items?itemName=vitest.explorer), you can streamline your testing process. It’s a good addition to Vitest's CLI by providing a graphical interface for debugging tests as well as running and visualizing code coverage. 

You can start debugging multiple test files or a single test by clicking on the play button with the bug icon: ![Debugging Tests Using The Vitest.Explorer Extension](https://blog.logrocket.com/wp-content/uploads/2024/05/debugging-from-vitest-explorer-extension.png) You can further enhance your workflow by incorporating code coverage analysis. This provides insight into the effectiveness of your tests by indicating which parts of your codebase are covered by tests and which are not. 

Before you start, you need to install a coverage provider. Depending on your preference, you can choose between the [v8](https://www.npmjs.com/package/@vitest/coverage-v8) or [istanbul](https://www.npmjs.com/package/@vitest/coverage-istanbul) packages:

```bash
$ npm i -D @vitest/coverage-v8
```

Then, you have to configure the coverage provider in `vitest.config.ts`:

```typescript
// ...
export default mergeConfig(
  viteConfig,
  defineConfig({
    test: {
        // ...
        root: fileURLToPath(new URL("./", import.meta.url)),
        coverage: {
            provider: "v8",
        },
    },
  }),
);
```

With your provider in place, you can run a test by clicking the **run test with coverage** button. The **Test Explorer** view provides a visual demonstration of this: ![Running Tests With Coverage From VS Code](https://blog.logrocket.com/wp-content/uploads/2024/05/run-tests-coverage-vs-code.png) Alternatively, you can run tests with coverage from the terminal (`$ vitest run --coverage`): ![Running Tests With Coverage From CLI](https://blog.logrocket.com/wp-content/uploads/2024/05/run-tests-coverage-cli.png) Another good way to work on your tests is to run the Vitest CLI in `watch mode`:

```typescript
$ vitest # watch mode is the default
```

By pressing the `H` key, you can open the menu to run failed tests or all tests. Whenever you save a file, the containing tests are rerun: ![Vitest CLI In Watch Mode](https://blog.logrocket.com/wp-content/uploads/2024/05/vitest-cli-watch-mode.png)

### Testing a service function performing a network call

Now, let's look at our first test. All the examples in this article are taken from the companion [GitHub project](https://github.com/doppelmutzi/companion-project-vitest-vue/tree/main/vitest-cheat-sheets). Consider the following service function, `fetchQuote`:

```typescript
// quote.service.ts
import type { Quote } from "./types/quote";
export async function fetchQuote() {
  const response = await fetch("https://dummyjson.com/quotes/random");
  const data: Quote = await response.json();
  return data;
}
```

`fetchQuote` performs a network call by using the [global `fetch` method](https://developer.mozilla.org/en-US/docs/Web/API/fetch). The returned fetch response is of type `Quote`. 

One approach is to create a spy to perform behavior verification after the test candidate, the `fetchQuote` function, is invoked. The spy tests whether the global fetch call has been called with the correct parameters. In addition, we can perform a state verification of the response:

```typescript
// quote.service.spy.test.ts
import { describe, it, expect, vi } from "vitest";
import { fetchQuote } from "./quote.service";
describe("fetchQuote", () => {
  it("should return a valid quote", async () => {
    const fetchSpy = vi.spyOn(globalThis, "fetch");
    // invoke the test candidate
    const response = await fetchQuote();
    // behavior verification
    expect(fetchSpy).toHaveBeenCalledWith(
      "https://dummyjson.com/quotes/random",
    );
    // state verification
    expect(response.quote).toBeDefined();
  });
});
```

As we've learned, a spy usually doesn’t alter the implementation, so a real network call is invoked. This isn’t ideal because it means that test runs are not deterministic. We can demonstrate this with a snapshot test by adding the following line:

```typescript
// ...
const response = await fetchQuote();
// ...
expect(response).toMatchSnapshot();
```

If you run the test twice, you’ll get a snapshot mismatch because the response object has different contents: ![Snapshot Testing Reveals That This Is Not A Deterministic Test](https://blog.logrocket.com/wp-content/uploads/2024/05/snapshot-testing-reveals-not-deterministic-test.png) In addition, we can use a rudimentary state verification because we can only test for the existence of the `quote` object:

```typescript
expect(response.quote).toBeDefined();
```

Let's look at a better approach for this testing scenario by mocking the service:

```typescript
// quote.service.mock.test.ts
import { describe, it, expect, vi } from "vitest";
import { fetchQuote } from "./quote.service";
import type { Quote } from "./types/quote";
vi.mock("./quote.service");
describe("fetchQuote", () => {
  it("should return a valid quote", async () => {
    const dummyQuote: Quote = {
      id: 1,
      quote: "This is a dummy quote",
      author: "Anonymous",
    };
    vi.mocked(fetchQuote).mockResolvedValue(dummyQuote);
    expect(await fetchQuote()).toEqual(dummyQuote);
  });
});
```

But wait — this isn’t a good test at all! It tests nothing because we replaced our test candidate with a mock. The test candidate has to be the unaltered original because we want to test its correct functionality. Let's mock the network call and define what should be returned by global `fetch`:

```typescript
// quote.service.mock.test.ts
import { describe, it, expect, vi } from "vitest";
import { fetchQuote } from "./quote.service";
import type { Quote } from "./types/quote";
describe("fetchQuote", () => {
  it("should return a valid quote", async () => {
    // just a dummy quote
    const dummyQuote: Quote = {
      id: 1,
      quote: "This is a dummy quote",
      author: "Anonymous",
    };
    // needs to contain the required properties 
    // https://developer.mozilla.org/en-US/docs/Web/API/Response 
    const mockResponse = {
      ok: true,
      statusText: "OK",
      json: async () => dummyQuote,
    } as Response;
    // this time mock global fetch instead of spying on it
    globalThis.fetch = vi.fn().mockResolvedValue(mockResponse);
    // state verification
    expect(await fetchQuote()).toEqual(dummyQuote);
  });
});
```

This is an example of a state verification by checking whether `fetchQuote`'s return value matches the response of the network call initiated by `fetch`. 

Don't worry — this test looks much more complicated than it is because we have to understand the signature of `fetch`. As you can see in the [documentation](https://developer.mozilla.org/en-US/docs/Web/API/fetch), it returns a promise that resolves to the [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object representing the response to your request. This is the `fetch` interface:

```typescript
  // node_modules/@types/node/globals.d.ts
  // ...
  function fetch(
    input: string | URL | globalThis.Request,
      init?: RequestInit,
  ): Promise<Response>;
  // ...
```

In our case, we defined a dummy object representing an object of type `Quote`. We return this dummy in the JSON response when the promise gets resolved. Therefore, we need to use Vitest's [`mockResolvedValue`](https://vitest.dev/api/mock#mockresolvedvalue), which is available for test doubles such as mocks and spies:

```typescript
  // ...
  const mockResponse = {
    // ...
    json: async () => dummyQuote,
  } as Response;
  globalThis.fetch = vi.fn().mockResolvedValue(mockResponse);
  // ...
```

We can also refactor our previous test (`quote.service.spy.test.ts`) in a way that the spy returns our fake response:

```typescript
  it("should return a valid quote by returning a fake response", async () => {
    const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue({
      ok: true,
      statusText: "OK",
      json: async () => ({ quote: "Hello, World!" }),
    } as Response);
    const response = await fetchQuote();
    expect(fetchSpy).toHaveBeenCalledWith(
      "https://dummyjson.com/quotes/random",
    );
    expect(response.quote).toBe("Hello, World!");
  });
```

Besides looking into the documentation or the globals' types, we can also make use of debugging to find out the internals of external modules.

### Testing the correct rendering of a Vue component

Testing the correct rendering of the `App.vue` component is an example of white box testing:

```vue
<template>
  <h1>My awesome dashboard</h1>
  <img :src="imageUrl" :alt="imgAlt" />
  <Counter />
  <TodoFromStore />
  <TodoFromComposable />
</template>
<script setup lang="ts">
// hide imports
const dashboardStore = useDashboardStore();
const imageUrl = ref("");
const imgAlt = ref("");
onMounted(async () => {
  const blob = await dashboardStore.createQuoteImageWithComposable();
  if (blob) {
    imageUrl.value = URL.createObjectURL(blob);
    imgAlt.value = dashboardStore.shortenedQuote;
  }
});
</script>
```

You will shortly find out that this component does not have a good design in terms of testability. This is how to test it:

```typescript
// App.test.ts
import { flushPromises, shallowMount } from "@vue/test-utils";
import { describe, expect, it, vi } from "vitest";
import App from "./App.vue";
import { createPinia, defineStore } from "pinia";
describe("App", () => {
  it("renders the image correctly", async () => {
    // mock URL.createObjectURL since it is an internal of the onMounted hook
    vi.stubGlobal("URL", { createObjectURL: () => "a nice URL" });
    // Create a mock store
    const useMockDashboardStore = defineStore("dashboard", () => ({
      createQuoteImageWithComposable: async () => {
        // Create a dummy blob
        const dummyBlob = new Blob();
        return Promise.resolve(dummyBlob);
      },
      shortenedQuote: "Dummy shortened quote",
    }));
    // init pinia and use the mock store
    const pinia = createPinia();
    useMockDashboardStore(pinia);
    // shallow mount the App component
    // only the first (component tree) level of Vue components are rendered
    const wrapper = shallowMount(App);
    // make sure to invoke onMounted lifecycle hook and resolve the promise
    await flushPromises();
    const imgEl = wrapper.find("img");
    expect(imgEl.attributes().alt).toBe("Dummy shortened quote");
    expect(imgEl.attributes().src).toBe("a nice URL");
    // renders only first child level of App component: h1, img, tags of included Vue components
    expect(wrapper.html()).toMatchSnapshot();
  });
});
```

The above code is complex, so let's break it down. The first question is how to deal with Vue components. We will combine Vitest with [Vue Test Utils](https://blog.logrocket.com/testing-vue-js-components-vue-test-utils/), which is a library of helper functions to help users test their Vue components. 

In this case, we can use either the [`mount`](https://test-utils.vuejs.org/api/#mount) or [`shallowMount`](https://test-utils.vuejs.org/api/#shallowMount) functions to mount the Vue component. We’ll opt for the latter because it stubs out all children of the `App` component. 

Testing the `App` component in isolation shouldn’t include implementation details of child components like `Counter`. Without `shallowMount`, we open up another can of worms because we also have to mock the dependencies of the child components. 

Switching from `shallowMount(App)` to `mount(App)` breaks our test because of the child component internals: ![Switching From Shallow Mount To Mount Breaks The Test](https://blog.logrocket.com/wp-content/uploads/2024/05/switching-between-shallow-mount-to-mount-breaks-test.png) We also need to set up a mock Pinia store (`dashboard`) because the `App` component accesses an action (`createQuoteImageWithComposable`) and a getter (`shortenedQuote`) in the `onMounted` lifecycle Hook:

```typescript
// Create a mock store
  const useMockDashboardStore = defineStore("dashboard", () => ({
    createQuoteImageWithComposable: async () => {
      const dummyBlob = new Blob();
      return Promise.resolve(dummyBlob);
    },
    shortenedQuote: "Dummy shortened quote",
  }));
  const pinia = createPinia();
  useMockDashboardStore(pinia);
  const wrapper = shallowMount(App);
```

Providing a mock store for testing doesn’t require using Vitest's API functions — we can just create a store with `defineStore` that exposes the API that `App`'s component uses. 

Inside the `onMounted` Hook, `store.createQuoteImageWithComposable()` needs to return a promise that resolves to a blob response, while `store.shortenedQuote` is just a string. 

Due to an unfavorable implementation, we need to stub `URL.createObjectURL` as it generates a URL by the returned blob:

```typescript
vi.stubGlobal("URL", { createObjectURL: () => "a nice URL" })
```

In a minute, I’ll suggest how to improve the `App` component to simplify this test. 

Because this component relies on a lifecycle hook, we need to utilize another API function of Vue Test Utils ([`flushPromises`](https://test-utils.vuejs.org/api/#flushPromises)). It is important to wait with asserting values until all DOM updates are done and all pending promises are resolved:

```typescript
await flushPromises();
```

This is why our test needs to be `async`:

```typescript
it("renders the image correctly", async () => {
  // ...
});
```

Now we can make our assertions:

```typescript
const imgEl = wrapper.find("img");
expect(imgEl.attributes().alt).toBe("Dummy shortened quote");
expect(imgEl.attributes().src).toBe("a nice URL");
// renders only first child level of App component: h1, img, tags of included Vue components
expect(wrapper.html()).toMatchSnapshot();
```

Because we used `shallowMount`, the snapshot looks like this:

```typescript
// __snapshots__/App.test.ts.snap
exports[`App > renders the image correctly 1`] = `
"<h1>My awesome dashboard</h1>
<img src="a nice URL" alt="Dummy shortened quote">
<counter-stub></counter-stub>
<todo-from-store-stub></todo-from-store-stub>
<todo-from-composable-stub></todo-from-composable-stub>"
`;
```

### Improving testability

Let’s make our lives easier by refactoring the `onMounted` Hook:

```typescript
// see AppRefactored.vue
onMounted(async () => {
  const image = await dashboardStore.createQuoteImageWithComposableRefactored();
  if (image) {
    imageUrl.value = image.url;
    imgAlt.value = image.altText;
  }
});
```

The `store` function is also refactored to provide an image URL and alt text right away:

```typescript
// return type of createQuoteImageWithComposableRefactored
 type Image = { url: string; altText: string };
```

The improved signature of the `store` function makes the `App` component easier to test because of better separation of concerns. The store method is completely in charge of providing the data to render. This means that we can get rid of stubbing out of `URL.createObjectURL` because it was extracted into the store method.

### Testing store actions in isolation

If you place complex logic into store actions and invoke them from Vue components, you can test this logic in isolation. Let's look at the example action used in the `App` component:

```typescript
// store.ts
const createQuoteImageWithComposable = async () => {
  const blob: Ref<Blob | null> = ref(null);
  const jsonState = await useFetch<Quote>(
    "https://dummyjson.com/quotes/random",
  );
  if (!jsonState.hasError) {
    currentQuote.value = jsonState.data;
    const blobState = await useFetch<Blob>(
      `https://dummyjson.com/image/768x80/008080/ffffff?text=${jsonState.data?.quote}`,
      { responseType: "blob" },
    );
    if (!blobState.hasError) {
      blob.value = blobState.data;
    }
  }
  return toValue(blob);
};
```

This action makes two network calls with an imported module (`useFetch`) to get two different responses. The challenge is to mock this imported `useFetch` composable in a way that the first call returns JSON and the second one a blob response. 

First, in order to mock the network calls in the arranging phase, we need to know the signature of the generic `useFetch` composable. We will look at the test of `useFetch` in a minute, but all we need to know right now is the signature:

```typescript
interface State<T> {
  isLoading: boolean;
  hasError: boolean;
  error: Error | null;
  data: T | null;
}
// signature of useFetch
function useFetch<T>(url: string, options?: {
    responseType?: "json" | "blob";
}): Promise<State<T>>
```

Our mock implementation of `useFetch` can ignore the arguments but we need to make sure to receive the correct fetch state objects:

```typescript
// store.test.ts
import { useFetch } from "./composables/useFetch";
// ...
vi.mock("./composables/useFetch");
// ...
it("createQuoteImageWithComposable should create a quote image by calling useFetch twice", async () => {
    // arrange
    const dummyJsonState = {
      data: { id: 1, quote: "Hello, World!", author: "Anonymous" },
      hasError: false,
    };
    const dummyBlob = new Blob();
    const mockedUseFetch = vi.mocked(useFetch) as Mock;
    mockedUseFetch
      .mockResolvedValueOnce(dummyJsonState)
      .mockResolvedValueOnce({ data: dummyBlob, hasError: false });
    // act / call the test candidate
    const blob = await store.createQuoteImageWithComposable();
    // state assertion
    expect(blob).toBe(dummyBlob);
    // behaviour assertions
    // check the arguments of the first useFetch call
    expect(mockedUseFetch.mock.calls\[0\][0]).toBe(
      "https://dummyjson.com/quotes/random",
    );
    // check the arguments of the second useFetch call
    expect(mockedUseFetch.mock.calls\[1\][0]).toBe(
      `https://dummyjson.com/image/768x80/008080/ffffff?text=${dummyJsonState.data.quote}`,
    );
  });
```

The comments inside of the previous snippet emphasize the different phases of a test: arrange (initialization of test doubles), act (invoking the test candidate), and assert (verification of correct behavior or result of test candidate). 

The approach used in the following test is to mock the whole module:

```typescript
vi.mock("./composables/useFetch")
```

[`vi.mock`](https://vitest.dev/api/vi.html#vi-mock) gets hoisted to the top of the file, so the position in the test file does not matter. In addition, we need to provide the actual import:

```typescript
import { useFetch } from "./composables/useFetch"`)
```

With that in place, we can create dummy return values that our mocked fetch calls should return:

```typescript
// arrange the test
const dummyJsonState = {
  data: { id: 1, quote: "Hello, World!", author: "Anonymous" },
  hasError: false,
};
const dummyBlob = new Blob();
const mockedUseFetch = vi.mocked(useFetch) as Mock;
mockedUseFetch
  .mockResolvedValueOnce(dummyJsonState)
  .mockResolvedValueOnce({ data: dummyBlob, hasError: false });
```

With [`vi.mocked`](https://vitest.dev/api/vi.html#vi-mocked), we get access to the mocked function and assign it to `mockedUseFetch`. With the help of [`mockResolvedValueOnce`](https://vitest.dev/api/mock.html#mockresolvedvalueonce), we can resolve the first promise with a JSON state and the second promise with a blob state. 

Finally, we can make our assertions:

```typescript
// state assertion
expect(blob).toBe(dummyBlob);
// ...
// check the arguments of the second useFetch call
expect(mockedUseFetch.mock.calls\[1\][0]).toBe(
`https://dummyjson.com/image/768x80/008080/ffffff?text=${dummyJsonState.data.quote}`,
);
```

### Testing composables in isolation

In the previous test, we mocked `useFetch` because it constitutes an external dependency of the test candidate (store action `createQuoteImageWithComposable`). Next, we'll test `useFetch` in isolation:

```typescript
// useFetch.ts
export async function useFetch<T>(
  url: string,
  options: { responseType?: "json" | "blob" } = { responseType: "json" },
) {
  const fetchState = reactive<State<T>>({
    isLoading: false,
    hasError: false,
    error: null,
    data: null,
  });
  try {
    fetchState.isLoading = true;
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(response.statusText);
    }
    fetchState.data =
      options.responseType === "json"
        ? await response.json()
        : await response.blob();
  } catch (err: unknown) {
    fetchState.hasError = true;
    fetchState.error = err as Error;
    // throw new Error(fetchState.error.message);
  } finally {
    fetchState.isLoading = false;
  }
  return fetchState;
}
```

The challenge is to deal with the global `fetch` function that makes actual network calls. For test stability and efficiency reasons, we do not want to perform actual fetch calls so, we’ll substitute it with a mock. 

The corresponding test shows one approach to mock global `fetch`, but there are multiple ways to do this as you will see in the cheat sheet section of this article. Let's look at the happy path first, where `useFetch` returns a JSON valid response, i.e., the network call is successful:

```typescript
// useFetch.test.ts
import { useFetch } from "./useFetch";
globalThis.fetch = vi.fn();
describe("useFetch", () => {
  it("should fetch data successfully and return the response", async () => {
    // arrange
    const dummyData = { message: "Hello, World!" };
    const mockResponse = {
      ok: true,
      statusText: "OK",
      json: async () => dummyData,
    } as Response;
    vi.mocked(fetch).mockResolvedValue(mockResponse);
    // act
    const response = await useFetch("https://api.example.com/data");
    // state assertions
    expect(response.isLoading).toBe(false);
    expect(response.hasError).toBe(false);
    expect(response.error).toBe(null);
    expect(response.data).toEqual(dummyData);
    // behavior assertions
    expect(vi.mocked(fetch)).toHaveBeenCalledTimes(1);
    expect(vi.mocked(fetch)).toHaveBeenCalledWith(
      "https://api.example.com/data",
    );
  });

  // ...
});
```

We set the properties of the response mock object to represent a successful network call. Because we want to receive JSON data, we add a `json` property to the `mockResponse` object. We create a dummy data object that gets returned when the promise is resolved (`dummyData`). Finally, we order Vitest to return our `mockResponse` when a fetch call is made:

```typescript
vi.mocked(fetch).mockResolvedValue(mockResponse);
```

In the acting phase, we will call the test candidate and store the mocked response in a variable:

```typescript
const response = await useFetch("https://api.example.com/data");
```

Finally, we make state and behavior assertions and test whether the fetch state looks as expected. 

Next is an example of testing a failing network call with the help of the [`mockRejectedValue`](https://vitest.dev/api/mock.html#mockresolvedvalue) API function:

```typescript
// useFetch.test.ts
import { useFetch } from "./useFetch";
globalThis.fetch = vi.fn();
// ...
it("should set the error state correctly when fetch request gets rejected", async () => {
  const errorMessage = "Network error";
  vi.mocked(fetch).mockRejectedValue(new Error(errorMessage));
  const response = await useFetch("https://api.example.com/data");
  expect(response.isLoading).toBe(false);
  expect(response.hasError).toBe(true);
  expect(response.error!.message).toEqual(errorMessage);
  expect(response.data).toBe(null);
});
```

### Testing timers

As the last example in this chapter, we’ll examine a rather complex composable, `useFetchTodoWithPolling`. The goal is to re-fetch to-dos at intervals after a defined number of milliseconds (parameter `pollingInterval`):

```typescript
// useFetchTodoWithPolling.ts
import { ref, type Ref } from "vue";
import { useFetch } from "./useFetch";
interface Todo {
  id: number;
  todo: string;
  completed: boolean;
  userId: number;
}
export const useFetchTodoWithPolling = (pollingInterval: number) => {
  const todo: Ref<Todo | null> = ref(null);
  const doPoll = ref(true);
  const poll = async () => {
    try {
      if (doPoll.value) {
        const fetchState = await useFetch<Todo>(
          "https://dummyjson.com/todos/random",
        );
        todo.value = fetchState.data;
        setTimeout(poll, pollingInterval);
      }
    } catch (err: unknown) {
      console.error(err);
    }
  };
  poll();
  const togglePolling = () => {
    doPoll.value = !doPoll.value;
    if (doPoll.value) {
      poll();
    }
  };
  return { todo, togglePolling, isPolling: doPoll };
};
```

The `return` object contains the fetched to-dos and allows us to pause and continue polling:

```typescript
// signature
const useFetchTodoWithPolling: (pollingInterval: number) => {
    todo: Ref<Todo | null>;
    togglePolling: () => void;
    isPolling: Ref<boolean>;
}
```

The following code snippets demonstrate a test that asserts the re-fetching functionality works as expected after five seconds and stops when `togglePolling` is called:

```typescript
// useFetchTodoWithPolling.test.ts
describe("useFetchTodoWithPolling", () => {
  const todo = {
    id: 1,
    todo: "vitest",
    completed: false,
    userId: 1,
  };
  vi.mock("./useFetch");
  const useFetchMocked = vi.mocked(useFetch);
  beforeEach(() => {
    // clear the mock to avoid side effects and start count with 0
    vi.clearAllMocks();
  });
  beforeAll(() => {
    vi.useFakeTimers();
  });
  afterAll(() => {
    vi.useRealTimers();
  });
  it("should fetch the API every 5 seconds until polling is stopped", async () => {
    useFetchMocked
      .mockResolvedValueOnce({
        isLoading: false,
        hasError: false,
        error: null,
        data: todo,
      })
      .mockResolvedValueOnce({
        isLoading: false,
        hasError: false,
        error: null,
        data: { ...todo, todo: "rules" },
      });
    const response = useFetchTodoWithPolling(5000);
    await flushPromises();
    expect(response.todo.value?.todo).toEqual("vitest");
    await vi.advanceTimersByTimeAsync(50);
    expect(response.todo.value?.todo).toEqual("vitest");
    await vi.advanceTimersByTimeAsync(4970);
    expect(response.todo.value?.todo).toEqual("rules");
    expect(useFetchMocked).toHaveBeenCalledTimes(2);
    response.togglePolling();
    expect(useFetchMocked).toHaveBeenCalledTimes(2);
  });
```

Let's discuss what the arrange and act phases have to look like. Regarding arranging the test setup, we need to mock `useFetch` because this external module is used to fetch to-dos. We saw this in the previous section. 

Another important aspect is how to handle the interval. Therefore, we utilize the Vitest API [`vi.useFakeTimers`](https://vitest.dev/api/vi.html#vi-usefaketimers) to work with fake timers in tests. Because we need fake timers for all tests in this test file, we put the code in a `beforeAll` Hook. It's also good practice to reset to real timers after all tests of the test suite and not rely on implicit resetting:

```typescript
  beforeEach(() => {
    // clear the mock to avoid side effects and start the count with 0 for every test
    vi.clearAllMocks();
  });
  beforeAll(() => {
    vi.useFakeTimers();
  });
  afterAll(() => {
    vi.useRealTimers();
  });
```

Clearing all mocks in `beforeEach` is also good practice to start every mock call count by `0` for every new test. In the end, tests are more semantic, easier to read, and less prone to errors. 

Next, as part of the acting phase, we’ll invoke our test candidate:

```typescript
const response = useFetchTodoWithPolling(5000);
```

The asserting phase is a bit more complex. Let's break it down:

```typescript
// the fetch function is called immediately
await flushPromises();
expect(response.todo.value?.todo).toEqual("vitest");
// timer hasn't advanced enough yet
await vi.advanceTimersByTimeAsync(50);
expect(response.todo.value?.todo).toEqual("vitest");
// timer has advanced more than 5 seconds now
await vi.advanceTimersByTimeAsync(4970);
expect(response.todo.value?.todo).toEqual("rules");
expect(useFetchMocked).toHaveBeenCalledTimes(2);
// stop polling
response.togglePolling();
// no fetching should happen now
expect(useFetchMocked).toHaveBeenCalledTimes(2);
```

`flushPromises` is required because the composable makes a fetch call once before any timer interval starts. Then we utilize another Vitest utility with [`vi.advanceTimersByTimeAsync`](https://vitest.dev/api/vi.html#vi-advancetimersbytimeasync). As you can see with the inline comments, now we establish different timer states and evaluate whether our fetch mock (`useFetchMocked`) has been called or not. 

Then we "act" again and stop polling (`response.togglePolling()`). Afterward, we evaluate one last time that no more fetch calls have been done.

## Vitest cheat sheet

This section will provide examples of various Vitest use cases that you can use for future reference. Unlike the detailed discussion above, the focus of this section is to show how to use Vitest’s API through simplified code snippets. 

Testing problems often have multiple solutions. The following examples will cover specific scenarios, such as mocking default imports, which can be challenging, especially for new developers. However, experienced developers familiar with other [testing libraries like Jest](https://blog.logrocket.com/jest-adoption-guide/) will also find this compilation useful.

### Default imports

The following examples demonstrate how to create test doubles of modules exposed as default exports:

```typescript
// default-func.ts
const getWithEmoji = (message: string) => {
  return `${message} 😎`;
};
export default getWithEmoji;
// default-obj.ts
export default {
  getWithEmoji: (message: string) => {
    return `${message} 😎`;
  },
};
// default-import.spec.ts
import { type Mock } from "vitest";
import getWithEmojiFunc from "./default-func";
import getWithEmojiObj from "./default-obj";
it("mock default function", () => {
  vi.mock("./default-func", () => {
    return {
      default: vi.fn((message: string) => `${message} 🥳`),
    };
  });
  expect(getWithEmojiFunc("hello world")).toEqual("hello world 🥳");
});
it("spy on default object's method", () => {
  const getWithEmojiSpy = vi.spyOn(getWithEmojiObj, "getWithEmoji") as Mock;
  const result = getWithEmojiSpy("spy kids");
  expect(result).toEqual("spy kids 😎");
  expect(getWithEmojiSpy).toHaveBeenCalledWith("spy kids");
});
```

The following shows how to create spies of a function exposed as a default import:

```typescript
// spy-default-func.spec.ts
import { type Mock } from "vitest";
import * as exports from "./default-func";
it("spy on default function", () => {
  const getWithEmojiSpy = vi.spyOn(exports, "default") as Mock;
  const result = getWithEmojiSpy("spy kids");
  expect(result).toEqual("spy kids 😎");
  expect(getWithEmojiSpy).toHaveBeenCalledWith("spy kids");
});
```

### Named imports

The next example shows how to create a spy of a function and a property, both exposed as named imports:

```typescript
// named-import-property.ts
export const magicNumber: number = 42;
// named-import-property.spec.ts
import * as exports from "./named-import-property";
it("mock property", () => {
  vi.spyOn(exports, "magicNumber", "get").mockReturnValue(41);
  expect(exports.magicNumber).toBe(41);
});
```

This next example demonstrates how to mock named imports:

```typescript
// named-import-func.ts
export const getWithEmoji = (message: string) => {
  return `${message} 😎`;
};
// named-import-func.spec.ts
import { getWithEmoji } from "./named-import-func";
it("mock named import (function)", () => {
  vi.mock("./named-import-func");
  const dummyMessage = "Hello, world!";
  const mockGetWithEmji = vi
    .mocked(getWithEmoji)
    .mockReturnValue(`${dummyMessage} 🤩`);
  const result = mockGetWithEmji(dummyMessage);
  expect(result).toBe(`${dummyMessage} 🤩`);
});
```

### Classes and prototypes

These examples demonstrate how to mock imported class modules (named and default imports):

```typescript
// default-class.ts
export default class Bike {
  ride() {
    return "original value";
  }
}
// named-class.ts
export class Car {
  drive() {
    return "original value";
  }
}
// class.spec.ts
import Bike from "./default-class";
import { Car } from "./named-class";
test("mock a method of a default import class", () => {
  vi.mock("./default-class", () => {
    const MyClass = vi.fn();
    MyClass.prototype.ride = vi.fn();
    return { default: MyClass };
  });
  const myMethodMock = vi.mocked(Bike.prototype.ride);
  myMethodMock
    .mockReturnValueOnce("mocked value")
    .mockReturnValueOnce("another mocked value");
  const myInstance = new Bike();
  let result = myInstance.ride();
  expect(result).toBe("mocked value");
  result = Bike.prototype.ride();
  expect(result).toBe("another mocked value");
  expect(myMethodMock).toHaveBeenCalledTimes(2);
});
test("mock a method of a named export class", () => {
  vi.mock("./named-class", () => {
    const MyClass = vi.fn();
    MyClass.prototype.drive = vi.fn();
    return { Car: MyClass };
  });
  const myMethodMock = vi.mocked(Car.prototype.drive);
  myMethodMock
    .mockReturnValueOnce("mocked value")
    .mockReturnValueOnce("another mocked value");
  const myInstance = new Car();
  let result = myInstance.drive();
  expect(result).toBe("mocked value");
  result = Car.prototype.drive();
  expect(result).toBe("another mocked value");
  expect(myMethodMock).toHaveBeenCalledTimes(2);
});
```

### Snapshot testing

[Snapshot testing](https://vitest.dev/api/expect.html#tomatchsnapshot) can be used for data objects. When a snapshot mismatch occurs and causes a test to fail, and if the mismatch is expected, you can press the `U` key to update the snapshot once:

```typescript
// data-snapshot.spec.ts
test("snapshot testing", () => {
  const data = {
    id: 1,
    name: "John Doe",
    email: "john.doe@example.com",
  };
  expect(data).toMatchSnapshot();
});
// Vitest snapshot example with an object like above but also a mocked function
test("snapshot testing with a mocked function", () => {
  const person = {
    id: 1,
    name: "John Doe",
    email: "john.doe@example.com",
    contact: vi.fn(),
  };
  expect(person).toMatchSnapshot();
});
```

This example shows how to use snapshot testing to record the render output of Vue components. Consider the following component:

```vue
<!-- AwesomeComponent.vue -->
<template>
  <h1 id="awesome-component">{{ greeting }}</h1>
</template>
<script setup lang="ts">
import { computed } from "vue";
const props = defineProps<{ name: string }>();
const greeting = computed(() => "Hello, " + props.name);
</script>
<style scoped>
#awesome-component {
  color: red;
}
</style>
```

The following demonstrates how to use Vue Test Utils to initialize the Vue component with a prop and create an HTML snapshot:

```typescript
// component-snapshot.spec.ts
import { mount } from "@vue/test-utils";
import AwesomeComponent from "./AwesomeComponent.vue";
test("renders component correctly", () => {
  const wrapper = mount(AwesomeComponent, {
    props: {
      name: "reader",
    },
  });
  expect(wrapper.html()).toMatchSnapshot();
});
```

### Composables and the Composition API

This example demonstrates how to test composables using the Composition API. To trigger an effect (because of `watchEffect`), you can make use of Vue's `nextTick` API:

```typescript
// composition-api.spec.ts
import { ref, watchEffect, nextTick } from "vue";
export function useCounter() {
  const count = ref(2);
  watchEffect(() => {
    if (count.value > 5) {
      count.value = 0;
    }
  });
  const increment = () => {
    count.value++;
  };
  return {
    count,
    increment,
  };
}
test("useCounter", async () => {
  const { count, increment } = useCounter();
  expect(count.value).toBe(2);
  increment();
  expect(count.value).toBe(3);
  increment();
  expect(count.value).toBe(4);
  increment();
  expect(count.value).toBe(5);
  increment();
  expect(count.value).toBe(6);
  // wait for the watcher to update the count
  await nextTick();
  expect(count.value).toBe(0);
});
```

### Vue lifecycle hooks and the Composition API

The following example shows a composable (`useCounter`) using the Composition API (`ref`, `watchEffect`) and a lifecycle hook (`onMounted`). The Vue component utilizes the composable's interface to update a counter on button clicks:

```typescript
// useCounter.ts
import { onMounted, ref, watchEffect } from "vue";
export function useCounter() {
  const count = ref(0);
  onMounted(() => {
    count.value = 2;
  });
  watchEffect(() => {
    if (count.value > 3) {
      count.value = 0;
    }
  });
  const increment = () => {
    count.value++;
  };
  return {
    count,
    increment,
  };
}
```
```vue
<!-- Counter.vue -->
<template>
  <div>count: {{ count }}</div>
  <button @click="increment">Increment</button>
</template>
<script setup lang="ts">
import { useCounter } from "./useCounter";
const { count, increment } = useCounter();
</script>
```

The following snippet shows how to mount a component, fire button click events, and verify that the used composable works as expected:

```typescript
// Counter.spec.ts
import { mount } from "@vue/test-utils";
import Counter from "./Counter.vue";
import { nextTick } from "vue";
test("renders component correctly", async () => {
  const wrapper = mount(Counter);
  const div = wrapper.find("div");
  expect(div.text()).toContain("count: 0");
  // make sure onMounted() is called by waiting until the next DOM update
  await nextTick();
  expect(div.text()).toContain("count: 2");
  const button = wrapper.find("button");
  button.trigger("click");
  button.trigger("click");
  await nextTick();
  expect(div.text()).toContain("count: 0");
});
```

### Partly mocked modules

The next example demonstrates how to mock only the relevant parts of an external module required for a test scenario (`getWithEmoji` function of `stringOperations` module). The other function (`log`) is irrelevant. Verification of the mock reveals that `log` is therefore not defined:

```typescript
// partly-mock-module.ts
const getWithEmoji = (message: string) => {
  return `${message} 😎`;
};
export const stringOperations = {
  log: (message: string) => console.log(message),
  getWithEmoji,
};
// partly-mock-module.spec.ts
import { stringOperations } from "./partly-mock-module";
it("mock method of imported object", () => {
  vi.mock("./partly-mock-module", () => {
    return {
      stringOperations: {
        getWithEmoji: vi.fn().mockReturnValue("Hello world 🤩"),
      },
    };
  });
  const mockGetWithEmoji = vi.mocked(stringOperations.getWithEmoji);
  const result = mockGetWithEmoji("Hello world");
  expect(mockGetWithEmoji).toHaveBeenCalledWith("Hello world");
  expect(result).toEqual("Hello world 🤩");
  expect(vi.isMockFunction(mockGetWithEmoji)).toBe(true);
  expect(stringOperations.log).not.toBeDefined();
});
```

This is an alternative variant providing both functions of `stringOperations`. The function `getWithEmoji` gets replaced by a mock implementation and `log` is kept in its original form:

```typescript
// partly-mock-module-restore-original.spec.ts
import { stringOperations } from "./partly-mock-module";
it("mock method of imported object and restore other original properties", () => {
  vi.mock("./partly-mock-module", async (importOriginal) => {
    const original =
      (await importOriginal()) as typeof import("./partly-mock-module");
    return {
      stringOperations: {
        log: original.stringOperations.log,
        getWithEmoji: vi.fn().mockReturnValue("Hello world 🤩"),
      },
    };
  });
  const { getWithEmoji: mockGetWithEmoji, log } = vi.mocked(stringOperations);
  const result = mockGetWithEmoji("Hello world");
  expect(mockGetWithEmoji).toHaveBeenCalledWith("Hello world");
  expect(result).toEqual("Hello world 🤩");
  expect(vi.isMockFunction(mockGetWithEmoji)).toBe(true);
  expect(log).toBeDefined();
  expect(vi.isMockFunction(log)).toBe(false);
});
```

### Access to external variables within mock implementations

In contrast to `vi.mock`, `vi.doMock` isn't hoisted to the top of a file. It's useful when you need to incorporate external variables inside mock implementations:

```typescript
// someModule.ts
export function someFunction() {
  return "original implementation";
}
// doMock.spec.ts
import { someFunction } from "./someModule";
it("original module", () => {
  const result = someFunction();
  expect(result).toEqual("original implementation");
});
it("doMock allows to use variables from scope", async () => {
  const dummyText = "dummy text";
  // vi.mock does not allow to use variables from the scope. It leads to errors like:
  // Error: [vitest] There was an error when mocking a module. If you are using "vi.mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file.
  // vi.doMock does not get hoisted to the top instead of vi.mock
  vi.doMock("./someModule", () => {
    return {
      someFunction: vi.fn().mockReturnValue(dummyText),
    };
  });
  // dynamic import is required to get the mocked module with vi.docMock
  const { someFunction: someFunctionMock } = await import("./someModule");
  const result = someFunctionMock();
  expect(someFunctionMock).toHaveBeenCalled();
  expect(result).toEqual(dummyText);
});
```

### Clean up test doubles

The following examples highlight how to use `mockClear`, `mockReset`, and `mockRestore` to clean up your tests, especially by testing doubles to avoid side effects:

```typescript
// add.ts
export const add = (a, b) => a + b;
// cleanup-mocks.spec.ts
import { add } from "./add";
test("mockClear", () => {
  const mockFunc = vi.fn();
  mockFunc();
  expect(mockFunc).toHaveBeenCalledTimes(1);
  // resets call history
  mockFunc.mockClear();
  mockFunc();
  expect(mockFunc).toHaveBeenCalledTimes(1);
});
test("mockReset vs mockRestore", async () => {
  const mockAdd = vi.fn(add).mockImplementation((a, b) => 2 * a + 2 * b);
  expect(vi.isMockFunction(mockAdd)).toBe(true);
  expect(mockAdd(1, 1)).toBe(4);
  expect(mockAdd).toHaveBeenCalledTimes(1);
  // resets call history and mock function returns undefined
  mockAdd.mockReset();
  expect(vi.isMockFunction(mockAdd)).toBe(true);
  expect(mockAdd(1, 1)).toBeUndefined();
  expect(mockAdd).toHaveBeenCalledTimes(1);
  // resets call history and mock function restores implementation to add
  mockAdd.mockRestore();
  expect(vi.isMockFunction(mockAdd)).toBe(true);
  expect(mockAdd(1, 1)).toBe(2); // original implementation
  expect(mockAdd).toHaveBeenCalledTimes(1);
});
```

### Auto-mocking modules and global mocks

Storing test doubles in a dedicated folder (`__mocks__`) allows for automatic mocking. The following example shows how to mock `axios` globally:

```typescript
// <root-folder>/__mocks__/axios.ts
import { vi } from "vitest";
const mockAxios = {
  get: vi.fn((url: string) =>
    Promise.resolve({ data: { urlCharCount: url.length } }),
  ),
  post: vi.fn(() => Promise.resolve({ data: {} })),
  // Add other methods as needed
};
export default mockAxios;
// axios.auto-mocking.spec.ts
import axios from "axios";
vi.mock("axios");
// auto-mocking example with <root-folder>/__mocks__ folder
test("mocked axios", async () => {
  const response = await axios.get("url");
  expect(response.data.urlCharCount).toBe(3);
  expect(axios.get).toHaveBeenCalledWith("url");
  expect(axios.delete).toBeUndefined();
  expect(vi.isMockFunction(axios.get)).toBe(true);
  expect(vi.isMockFunction(axios.post)).toBe(true);
  expect(vi.isMockFunction(axios.delete)).toBe(false);
});
// use actual axios in test
test("can get actual axios", async () => {
  const ax = await vi.importActual<typeof axios>("axios");
  expect(vi.isMockFunction(ax.get)).toBe(false);
});
```

The following example demonstrates how you can mock `axios` only for individual tests:

```typescript
// axios.spec.ts
import axios from "axios";
test("mocked axios", async () => {
  const { default: ax } =
    await vi.importMock<typeof import("axios")>("axios");
  const response = await ax.get("url");
  expect(ax.get).toHaveBeenCalledWith("url");
  expect(response.data.urlCharCount).toEqual(3);
});
test("actual axios is not mocked", async () => {
  expect(vi.isMockFunction(axios.get)).toBe(false);
});
```

### Date and time

Vitest provides the [`vi.setSystemTime`](https://vitest.dev/api/vi.html#vi-setsystemtime) method to fake the system time:

```typescript
// date-and-time.spec.ts
const getCurrentTime = () => new Date().toTimeString().slice(0, 5);
it("should return the correct system time", () => {
  vi.setSystemTime(new Date("2024-04-04 15:17"));
  expect(getCurrentTime()).toBe("15:17");
  // cleanup
  vi.useRealTimers();
});
```

### Rejected promises and errors

The following examples show how to verify thrown exceptions:

```typescript
// error.spec.ts
test("should throw an error", () => {
  expect(() => {
    throw new Error("Error message");
  }).toThrow("Error message");
});
test("should throw an error after rejected fetch", async () => {
  const errorMessage = "Network error";
  vi.stubGlobal(
    "fetch",
    vi.fn(() => Promise.reject(new Error(errorMessage))),
  );
  await expect(fetch("https://api.example.com/data")).rejects.toThrow(
    "Network error",
  );
});
test("more sophisticated example of expecting error and resolved value", async () => {
  const errorMessage = "Network error";
  class MyError extends Error {
    constructor(message: string) {
      super(message);
      this.name = "MyError";
    }
  }
  globalThis.fetch = vi.fn().mockRejectedValue(new MyError(errorMessage));
  await expect(fetch("https://api.example.com/data")).rejects.toThrowError(
    "Network error",
  );
  await expect(fetch("https://api.example.com/data")).rejects.toThrowError(
    Error,
  );
  await expect(fetch("https://api.example.com/data")).rejects.toThrowError(
    /Network/,
  );
  globalThis.fetch = vi.fn().mockResolvedValue("success");
  const response = await fetch("https://api.example.com/data");
  expect(response).toBe("success");
});
```

### Replace global fetch

The following shows different methods for replacing global `fetch` with test doubles:

```typescript
// fetch.spec.ts
test("variant with globalThis.fetch", async () => {
  const dummyData = { message: "hey" };
  const mockResponse = {
    ok: true,
    statusText: "OK",
    json: async () => dummyData,
  } as Response;
  globalThis.fetch = vi.fn().mockResolvedValue(mockResponse);
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  expect(data).toEqual(dummyData);
});
test("variant with globalThis.fetch and vi.mocked", async () => {
  const dummyBlob = new Blob();
  const mockResponse = {
    ok: true,
    statusText: "OK",
    blob: async () => dummyBlob,
  } as Response;
  globalThis.fetch = vi.fn();
  vi.mocked(fetch).mockResolvedValue(mockResponse);
  const response = await fetch("https://api.example.com/data");
  const data = await response.blob();
  expect(response.blob).toBeDefined();
  expect(data).toEqual(dummyBlob);
  expect(response.json).not.toBeDefined();
});
test("variant with vi.stubGlobal", async () => {
  const dummyData = { data: "hey" };
  const dummyBlob = new Blob();
  vi.stubGlobal(
    "fetch",
    vi.fn(() =>
      Promise.resolve({
        blob: async () => dummyBlob,
        json: () => dummyData,
      }),
    ),
  );
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  const blob = await response.blob();
  expect(response).toEqual({
    json: expect.any(Function),
    blob: expect.any(Function),
  });
  expect(data).toEqual(dummyData);
  expect(blob).toEqual(dummyBlob);
});
test.todo("variant with rejected fetch", async () => {
  // see error.spec.ts
});
```

### Parameterize tests

With Vitest's [`test.each`](https://vitest.dev/api/#test-each) API, you can pass a different set of data into tests:

```typescript
// run tests 3x with different string values
const inputs = ["Hello", "world", "!"];
test.each(inputs)("Testing string length", (input) => {
  expect(input.length).toBeGreaterThan(0);
});
test.each(inputs)("Testing string lengths of %s", (input) => {
  expect(input.length).toBeGreaterThan(0);
});
```

### Stub `globalThis` and `window` with `stubGlobal`

[`vi.stubGlobal`](https://vitest.dev/api/vi.html#vi-stubglobal) can be used to mock different global properties such as `console.log` or `fetch`. To stub `window` properties, you need to use [`jsdom` or `happy-dom`](https://vitest.dev/guide/environment#test-environment):

```typescript
test("Math example", () => {
  vi.stubGlobal("Math", { random: () => 0.5 });
  const result = Math.random();
  expect(result).toBe(0.5);
});
test("Date example", () => {
  vi.stubGlobal(
    "Date",
    class {
      getTime() {
        return 1000;
      }
    },
  );
  expect(new Date().getTime()).toBe(1000);
  expect(new Date().getTime()).not.toBe(2000);
});
test("console example", () => {
  vi.stubGlobal("console", {
    log: vi.fn(),
    error: vi.fn(),
  });
  console.log("Hello, World!");
  console.error("An error occurred!");
  const log = vi.mocked(console.log);
  const error = vi.mocked(console.error);
  expect(log).toHaveBeenCalledWith("Hello, World!");
  expect(error).toHaveBeenCalledWith("An error occurred!");
  expect(vi.isMockFunction(log)).toBe(true);
  expect(vi.isMockFunction(error)).toBe(true);
});
test("window example", () => {
  vi.stubGlobal("window", {
    innerWidth: 1024,
    innerHeight: 768,
  });
  expect(window.innerWidth).toBe(1024);
  expect(window.innerHeight).toBe(768);
});
test.todo("fetch example", () => {
  // see fetch.spec.ts
});
```

### Verification of test doubles

Vitest features many useful API functions to verify invocations of test doubles:

```typescript
test("verify return value of a mocked function", () => {
  const mockFn = vi.fn();
  // Set the return value of the mock function
  mockFn.mockReturnValue("mocked value");
  // Call the mock function
  const result = mockFn();
  // Verify that the return value has a particular value
  expect(result).toBe("mocked value");
  // Verify that the return value is of a particular type
  expect(typeof result).toBe("string");
});
test("verify invocations of a mocked function with any argument", () => {
  const mockFn = vi.fn();
  // Call the mock function with different arguments
  mockFn("arg1", 123);
  mockFn("arg2", { key: "value" });
  // Verify that the mock function was called with any string as the first argument
  expect(mockFn).toHaveBeenCalledWith(expect.any(String), expect.anything());
  // Verify that the mock function was called with any number as the second argument
  expect(mockFn).toHaveBeenCalledWith(expect.anything(), expect.any(Number));
  // Verify that the mock function was called with any object as the second argument
  expect(mockFn).toHaveBeenCalledWith(expect.anything(), expect.any(Object));
});
test("verify invocations of a mocked function with calls array", () => {
  const mockFn = vi.fn();
  // Call the mock function with different arguments
  mockFn("arg1", "arg2");
  mockFn("arg3", "arg4");
  // Verify that the mock function was called
  expect(mockFn).toHaveBeenCalled();
  // Verify that the mock function was called exactly twice
  expect(mockFn).toHaveBeenCalledTimes(2);
  // Verify that the mock function was called with specific arguments
  expect(mockFn).toHaveBeenCalledWith("arg1", "arg2");
  expect(mockFn).toHaveBeenCalledWith("arg3", "arg4");
  // Verify the order of calls and their arguments using the mock array
  expect(mockFn.mock.calls[0]).toEqual(["arg1", "arg2"]);
  expect(mockFn.mock.calls[1]).toEqual(["arg3", "arg4"]);
  // clear the mock call history
  mockFn.mockClear();
  // Verify that the mock function was not called after resetting
  expect(mockFn).not.toHaveBeenCalled();
  // Call the mock function again with different arguments
  mockFn("arg5", "arg6");
  // Verify that the mock function was called once after resetting
  expect(mockFn).toHaveBeenCalledTimes(1);
  // Verify that the mock function was called with specific arguments after resetting
  expect(mockFn).toHaveBeenCalledWith("arg5", "arg6");
  // Verify the order of calls and their arguments using the mock array after resetting
  expect(mockFn.mock.calls[0]).toEqual(["arg5", "arg6"]);
});
test("verify invocations of a mocked function with a specific object", () => {
  interface MyInterface {
    key: string;
  }
  const mockFn = vi.fn();
  // Call the mock function with an object that matches the MyInterface interface
  mockFn({ key: "value", extraProp: "extra" });
  // Verify that the mock function was called with an object containing a specific key-value pair
  expect(mockFn).toHaveBeenCalledWith(
    expect.objectContaining({ key: "value" } as MyInterface),
  );
});
```

## Conclusion

While testing may require an upfront investment of time and effort, the long-term benefits it provides in terms of code quality, maintainability, and developer understanding make it an invaluable practice in software development. This is why you should invest your time in learning Vitest to improve the quality of your JavaScript project.

---

##Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, [try LogRocket](https://lp.logrocket.com/blg/vue-signup).

[![LogRocket Signup](https://files.readme.io/00591d0-687474703a2f2f692e696d6775722e636f6d2f6a3049327856572e706e67.png)](https://lp.logrocket.com/blg/vue-signup)

[LogRocket](https://lp.logrocket.com/blg/vue-signup) is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps — [start monitoring for free](https://lp.logrocket.com/blg/vue-signup).