OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

How to clear/reset mocks in Vitest

  • Thread starter Thread starter n1md7
  • Start date Start date
N

n1md7

Guest
I have a simple composable useRoles which I need to test

Code:
import { computed } from "vue";
import { useStore } from "./store";

export default function useRoles() {
  const store = useStore();

  const isLearner = computed(() => store.state.profile.currentRole === "learner");

  return {
    isLearner
  };
}

My approach of testing it is the following

Code:
import { afterEach, expect } from "vitest";
import useRoles from "./useRoles";

describe("useRoles", () => {
  afterEach(() => {
    vi.clearAllMocks();
    vi.resetAllMocks();
  });

  it("should verify values when is:Learner", () => { // works
    vi.mock("./store", () => ({
      useStore: () => ({
        state: {
          profile: {
            currentRole: "learner"
          },
        },
      }),
    }));

    const { isLearner } = useRoles();

    expect(isLearner.value).toBeTruthy();
  });

  it("should verify values when is:!Learner", () => { //fails
    vi.mock("./store", () => ({
      useStore: () => ({
        state: {
          profile: {
            currentRole: "admin"
          },
        },
      }),
    }));

    const { isLearner } = useRoles(); // Values are from prev mock

    expect(isLearner.value).toBeFalsy();
  });
});

And useStore is just a simple function that I intended to mock

Code:
export function useStore() {
  return {/**/};
}

The first test runs successfully, it has all the mock values I implemented but the problem is that it's not resetting for each test (not resetting at all). The second test has the old values from the previous mock.

I have used

Code:
vi.clearAllMocks();
vi.resetAllMocks();

but for some reason clear or reset is not happening.

How can I clear vi.mock value for each test?

Solution​


As it turned out I should not be called vi.mock multiple times. That was the main mistake

Substitutes all imported modules from provided path with another module. You can use configured Vite aliases inside a path. The call to vi.mock is hoisted, so it doesn't matter where you call it. It will always be executed before all imports.
Vitest statically analyzes your files to hoist vi.mock. It means that you cannot use vi that was not imported directly from vitest package (for example, from some utility file)

Docs

My fixed solution is below.

Code:
import useRoles from "./useRoles";
import { useStore } from "./store"; // Required the mock to work

vi.mock("./store");

describe("useRoles", () => {
  afterEach(() => {
    vi.clearAllMocks();
    vi.resetAllMocks();
  });

  it("should verify values when is:Learner", () => {
    // @ts-ignore it is a mocked instance so we can use any vitest methods
    useStore.mockReturnValue({
      state: {
        profile: {
          currentRole: "learner",
        },
      },
    });

    const { isLearner } = useRoles();

    expect(isLearner.value).toBeTruthy();
  });

  it("should verify values when is:!Learner", () => {
    // You need to use either @ts-ignore or typecast it
    // as following (<MockedFunction<typeof useStore>>useStore)
    // since original function has different type but vitest mock transformed it
    (<MockedFunction<typeof useStore>>useStore).mockReturnValue({
      state: {
        profile: {
          currentRole: "admin",
        },
      },
    });

    const { isLearner } = useRoles();

    expect(isLearner.value).toBeFalsy();
  });
});

vitest = v0.23.0

<p>I have a simple composable <code>useRoles</code> which I need to test</p>
<pre class="lang-js prettyprint-override"><code>import { computed } from "vue";
import { useStore } from "./store";

export default function useRoles() {
const store = useStore();

const isLearner = computed(() => store.state.profile.currentRole === "learner");

return {
isLearner
};
}

</code></pre>
<p>My approach of testing it is the following</p>
<pre class="lang-js prettyprint-override"><code>import { afterEach, expect } from "vitest";
import useRoles from "./useRoles";

describe("useRoles", () => {
afterEach(() => {
vi.clearAllMocks();
vi.resetAllMocks();
});

it("should verify values when is:Learner", () => { // works
vi.mock("./store", () => ({
useStore: () => ({
state: {
profile: {
currentRole: "learner"
},
},
}),
}));

const { isLearner } = useRoles();

expect(isLearner.value).toBeTruthy();
});

it("should verify values when is:!Learner", () => { //fails
vi.mock("./store", () => ({
useStore: () => ({
state: {
profile: {
currentRole: "admin"
},
},
}),
}));

const { isLearner } = useRoles(); // Values are from prev mock

expect(isLearner.value).toBeFalsy();
});
});

</code></pre>
<p>And <code>useStore</code> is just a simple function that I intended to mock</p>
<pre class="lang-js prettyprint-override"><code>export function useStore() {
return {/**/};
}
</code></pre>
<p>The first test runs successfully, it has all the mock values I implemented but the problem is that it's not resetting for each test (not resetting at all). The second test has the old values from the previous mock.</p>
<p>I have used</p>
<pre class="lang-js prettyprint-override"><code>vi.clearAllMocks();
vi.resetAllMocks();
</code></pre>
<p>but for some reason clear or reset is not happening.</p>
<p>How can I clear <code>vi.mock</code> value for each test?</p>
<h1>Solution</h1>
<p>As it turned out I should not be called <code>vi.mock</code> multiple times. That was the main mistake</p>
<blockquote>
<p>Substitutes all imported modules from provided path with another module. You can use configured Vite aliases inside a path. The call to vi.mock is hoisted, so it doesn't matter where you call it. It will always be executed before all imports.</p>
</blockquote>
<blockquote>
<p>Vitest statically analyzes your files to hoist vi.mock. It means that you cannot use vi that was not imported directly from vitest package (for example, from some utility file)</p>
</blockquote>
<p><a href="https://vitest.dev/api/vi.html#vi-mock" rel="noreferrer">Docs</a></p>
<p>My fixed solution is below.</p>
<pre><code>import useRoles from "./useRoles";
import { useStore } from "./store"; // Required the mock to work

vi.mock("./store");

describe("useRoles", () => {
afterEach(() => {
vi.clearAllMocks();
vi.resetAllMocks();
});

it("should verify values when is:Learner", () => {
// @ts-ignore it is a mocked instance so we can use any vitest methods
useStore.mockReturnValue({
state: {
profile: {
currentRole: "learner",
},
},
});

const { isLearner } = useRoles();

expect(isLearner.value).toBeTruthy();
});

it("should verify values when is:!Learner", () => {
// You need to use either @ts-ignore or typecast it
// as following (<MockedFunction<typeof useStore>>useStore)
// since original function has different type but vitest mock transformed it
(<MockedFunction<typeof useStore>>useStore).mockReturnValue({
state: {
profile: {
currentRole: "admin",
},
},
});

const { isLearner } = useRoles();

expect(isLearner.value).toBeFalsy();
});
});

</code></pre>
<p><code>vitest = v0.23.0</code></p>
 
Top