vuejs/vitest: How to test/mock composition functions
Asked Answered
P

1

9

I’m kinda stuck with testing composition-functions. I’m using vitest with @vue/test-utils, but also found no good way to get it to work with jest.

My issue is: How do I test composable functions, which use other composable functions? And how do I correctly mock the functions return values?

Let’s say I have a composition like this:

import {useExternalComposition} from '@/composables/externalComposition'

export function useFoo() {
  function isConditionTrue(condition) {
    if (condition) {
      return false;
    }
    return true;
  }
  
  async function bar() {
    const { externalFunction1, externalFunction2} = useExternalComposition();
    const isTrue = isConditionTrue(true);
    try {
      if (isTrue) {
        await externalFunction1();
        return;
      } 
      await externalFunction2();
    } catch(e) {
      console.error(e);
    }
  }
  
  return {
    bar, 
    isConditionTrue
  }
}

With vitest I was not able to figure out a way, to correctly test bar, in detail to mock results from isConditionTrue and externalFunction1 and externalFunction2.

This was my best approach for now:

it('should call isConditionTrue', async function () {
  const useFooVar = useFoo();
  const spy = vi
    .spyOn(useFooVar, 'isConditionTrue')
    .mockImplementationOnce(() => true);

  expect(spy.getMockName()).toEqual('isConditionTrue'); // true

  await useFooVar.bar();
  expect(spy).toHaveBeenCalled(); // AssertionError: expected "isConditionTrue" to be called at least once
});

What do I miss?

I tried multiple ways of mocking/spying functions, and expected to have a correct assertion

Parasynapsis answered 9/9, 2022 at 7:12 Comment(1)
Have you ever solved this issue?Felske
T
4

For mocking I am doing this:

import { composableOne } from '~/composables/composableOne';
import { describe, it, expect, afterEach, vi } from 'vitest';

describe('composableOne', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });
  it('should return true', async () => {
    // composableOne uses composableTwo
    vi.doMock('~/composables/composableTwo', () => {
      return {
        composableTwo: vi.fn().mockReturnValue('my new mocked return value'),
      };
    });
    const { composableOne } = await import('~/composables/composableOne');
    // then expect whatever you want based on the new value
    expect(composableOne()).toBe(true);
  });
});

Note that you could use vi.mock, but this hoists the declaration to the top of the file. So if you tried to mock the same thing twice. It doesn't work. This is the reason I use vi.doMock, which will not hoist the declaration, but apply the mock the next time your composable is imported. This is the reason for the import after the mock, as this import is aware of the mock coming before it.

More information here

Thenar answered 23/8, 2023 at 21:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.