8

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 });
    });
});
floriankapaun
  • 472
  • 1
  • 4
  • 19

2 Answers2

9

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;
floriankapaun
  • 472
  • 1
  • 4
  • 19
5

I also ran in to this problem with using Vite. After a lot of trail and error I have managed to get the mocking of a module function working by using the following code:

vi.mock('@/models/generated/graphql')

describe('MyComponent works as expected', () => {
  it('Shows loading when loading', async () => {
    const graphql = await import('@/models/generated/graphql')
    graphql.useGetAllQuery = vi.fn().mockReturnValue({ loading: true, error: null, data: null })

    render(<MyComponent />)

    expect(screen.findByTestId('my-component-loading')).toBeTruthy()
  })
}

The function that I am mocking is this one (which is auto generated for my Graphql service):

export function useGetAllQuery(baseOptions?: Apollo.QueryHookOptions<GetAllQuery, GetAllQueryVariables>) {
        const options = {...defaultOptions, ...baseOptions}
        return Apollo.useQuery<GetAllQuery, GetAllQueryVariables>(GetAllDocument, options);
      }

I do not really understand why this works, but it does. I hope that this code snippet might help some.