Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BeforeEach not being executed in Vitest (Node.Js unit tests)

I'm writing unit tests for a Node.js module in Typescript, using Vitest. In one of the tests, the function that I'm testing is supposed to call another function (in a different module), so I've created a mocked version of that function.

Here is the setup code:

// unit tests for tested module

import _ from 'lodash';
import {
  function_to_mock1,
  function_to_mock2,
} from '../../../a/b/secondary_module.js';
import {
  DOCUMENT
} from './documents.js';

let requestIdCounter = 0;
const TEST_OPTIONS: BaseOptions = {
  logger: LOGGER,
  logLevel: 'silent',
  requestId: 'dummy-request-id',
};

vi.mock(
  '../../../a/b/secondary_module.js',
  async (importOriginal) => {
    return {
      ...(await importOriginal<
        typeof import('../../../a/b/secondary_module.js')
      >()),
      function_to_mock1: vi.fn(),
      function_to_mock2: vi.fn(),
    };
  },
);

const function1Mock = vi.mocked(function_to_mock1);
const function2Mock = vi.mocked(function_to_mock2);

function1Mock.mockImplementation(
  async () => {
    console.log(`function1Mock called with request id ${requestId}`);
    return fakeResult();
  },
);

function setupTest(targetDocument: Document): void {
  const mockDocument = _.cloneDeep(targetDocument);
  function2Mock.mockImplementation(async () => mockDocument);
}

const TEST_ASSETS: Assets[] = [
  // asset1
  {...},
  // asset2
  {...},
  // asset3
  {...},
  // asset4
  {...},
];

This is the actual code being tested:

// tested module

export async function testedFunction(
  { sourceDocument, assets }: TestedFunctionInput,
  options: BaseOptions,
): Promise<Document> {
  ...

  let targetDocument = cloneDeep(sourceDocument);
  const {...} = assets;

  ...

  targetDocument = await internalFunction(
    {
      sourceDocument,
      targetDocument,
      ...
    },
    options,
  );

  if (condition) {
    someOtherFunction(input);
  }

  ...

  return targetDocument;
}

async function internalFunction(
  {...}: InternalFunctionInput,
  options: BaseOptions,
): Promise<Document> {
  try {
    ...

    const {
       property: { innerProperty },
     } = await function_to_mock1(...);

    ...

    targetDocument = await function_to_mock2(...);

    return targetDocument;
  } catch (err) {
    throw new UnprocessableEntityError('An error occurred');
  }
}

Here is the test code. I commented out the contents of beforeEach, beforeAll, and afterAll because they were causing some weird behaviors. For example, the vi.restoreAllMocks() in afterAll was being executed after each iteration of the parameterized test, instead of being executed after all the tests had finished running.

// unit tests for tested module - continued

describe.each(TEST_ASSETS)('myModule', async (assetObject: Assets) => {
  beforeAll(() => {
    // ...
  });

  beforeEach(() => {
    // vi.clearAllMocks();
    // ...
  });

  afterAll(() => {
    // vi.restoreAllMocks();
  });

  it('should do something', async () => {
      //Arrange
      const inputDocument: Document = _.cloneDeep(DOCUMENT);
      setupTest(inputDocument);

      //Act
      const actualResultDocument = await testedFunction(
        {
          source: inputDocument,
          assets: assetObject,
        },
        { ...TEST_OPTIONS, requestId: requestIdCounter.toString() },
      );
      requestIdCounter++;
      
      //Assert
      expect(actualResultDocument).toEqual(inputDocument);
      expect(function1Mock).toHaveBeenCalledOnce(); 
      expect(function2Mock).toHaveBeenCalledOnce();

      console.log('made it past the mock assertions!');
      ... // more assertions

This is a parameterized test, and it runs on 4 different data sets. This is the test output when I run it on all 4 sets:

function1Mock called with request id 0
made it past the mock assertions!
function1Mock called with request id 1
function1Mock called with request id 2
function1Mock called with request id 3

Judging by some console.log statements I've since removed, the second assertion (the one for function1Mock) fails after the first test.

Edit: I think I know what the problem is. function1Mock gets called once every time the test is run, so by the second iteration of the same test, the assertion expect(function1Mock).toHaveBeenCalledOnce() fails. So what I tried doing is clearing all mocks and setting up the mock implementation again in beforeEach, thus:

console.log('running BeforeEach()');
    vi.clearAllMocks();
    function1Mock.mockImplementation(async () => 
returnFakeResult());

The problem is, it looks like beforeEach() is not being executed at all. The log statement isn't being printed out to the console, and I'm baffled as to why.

I've tried reading the Vitest documentation, googling it and even asking ChatGPT, but I can't seem to find an explanation for why beforeEach isn't being run. Can anyone help me out? Thanks!

like image 677
Maya Avatar asked Sep 17 '25 00:09

Maya


1 Answers

It turns out the answer was very simple. BeforeEach was not being executed because it was accidentally imported from the node:test package and not from the vitest package. Just goes to show that sometimes it's worth going over the obvious stuff.

like image 79
Maya Avatar answered Sep 19 '25 15:09

Maya