Skip to content
Advertisement

Vitest mock modules function in only one test and use the actual function in others

The following is an abstraction of my problem and thus does not make too much sense:

Given I have a simple utility callMethodIf that’s returning the return of another imported method (blackbox).

~~/utils/call-method-if.js:

import { blackbox } from '~~/utils/blackbox';

export const callMethodIf = (condition) => {
    return blackbox(condition);
};

~~/utils/blackbox.js:

export const blackbox = (condition) => {
    return { called: condition };
};

How would I run one test case which calls the actual implementation of blackbox() and another one where I mock the return value of blackbox()?

I tried to do it that way:

import { describe, expect, it } from 'vitest';

import { callMethodIf } from '~~/utils/call-method-if';

describe('Call method if', () => {
    it('returns "called: true" if condition is true', () => {
        const result = callMethodIf(true);
        expect(result).toEqual({ called: true });
    });

    it('returns mocked blackbox return object', () => {
        vi.mock('~~/utils/blackbox', () => ({
            blackbox: vi.fn().mockReturnValue({ mock: true })
        }));
        const result = callMethodIf(false);
        expect(result).toEqual({ mock: true });
    });
});

Both tests work if I run only one of them, but they don’t work when combined.

Running vi.clearAllMocks() or vi.resetAllMocks() don’t help.

Defining a global mock and overwriting it in my first test doesn’t work either:

import { describe, expect, it } from 'vitest';

import { callMethodIf } from '~~/utils/call-method-if';

vi.mock('~~/utils/blackbox', () => ({
    blackbox: vi.fn().mockReturnValue({ mock: true })
}));

describe('Call method if', () => {
    it('returns "called: true" if condition is true', () => {
        vi.mock('~~/utils/blackbox', async () => ({
            blackbox: (await vi.importActual('~~/utils/blackbox')).blackbox
        }));
        const result = callMethodIf(true);
        expect(result).toEqual({ called: true });
    });

    it('returns mocked blackbox return object', () => {
        const result = callMethodIf(false);
        expect(result).toEqual({ mock: true });
    });
});

Advertisement

Answer

Okay, after lots of trial and error I finally got it to work. I can’t really tell why my previous tries do not work tough.

Working solution:

import { describe, expect, it } from 'vitest';

import { callMethodIf } from '~~/utils/call-method-if';

vi.mock('~~/utils/blackbox');

describe('Call method if', () => {
    it('returns "called: true" if condition is true', async () => {
        const blackbox = await import('~~/utils/blackbox');
        blackbox.blackbox = (await vi.importActual('~~/utils/blackbox')).blackbox;
        const result = callMethodIf(true);
        expect(result).toEqual({ called: true });
    });

    it('returns mocked blackbox return object', async () => {
        const blackbox = await import('~~/utils/blackbox');
        blackbox.blackbox = vi.fn().mockReturnValue({ mock: true });
        const result = callMethodIf(false);
        expect(result).toEqual({ mock: true });
    });
});

When using TypeScript consider typing the importActual() return like that:

blackbox.blackbox = (await vi.importActual<typeof import('~~/utils/blackbox')>('~~/utils/blackbox')).blackbox;
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement