typescript - BeforeEach nu este executat în Vitest (teste unitare Node.Js)


Scriu teste unitare pentru un modul Node.js în Typescript, folosind Vitest. În unul dintre teste, funcția pe care o testez ar trebui să apeleze o altă funcție (dintr-un modul diferit), așa că am creat o versiune falsificată a acelei funcții.

Iată codul de configurare:

// 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
  {...},
];

Acesta este codul efectiv care este testat:

// 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');
  }
}

Iată codul de test. Am comentat conținutul din beforeEach, beforeAll și afterAll pentru că provocau unele comportamente ciudate. De exemplu, vi.restoreAllMocks() din afterAll era executat după fiecare iterație a testului parametrizat, în loc să fie executat după ce toate testele s-au terminat.

// 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

Acesta este un test parametrizat și rulează pe 4 seturi diferite de date. Acesta este rezultatul de test când îl rulez pe toate cele 4 seturi:

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

Judecând după unele afirmații console.log pe care le-am eliminat între timp, a doua afirmație (cea pentru function1Mock) eșuează după primul test.

Editare: Cred că știu care e problema. function1Mock este apelat o dată de fiecare dată când testul este rulat, astfel încât la a doua iterație a aceluiași test, aserția expect(function1Mock).toHaveBeenCalledOnce() eșuează. Așa că ce am încercat a fost să șterg toate mock-urile și să setez din nou implementarea mock-ului în beforeEach, astfel:

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

Problema este că se pare că before Each() nu este executat deloc. Extrasul de jurnal nu este tipărit pe consolă și nu înțeleg de ce.

Am încercat să citesc documentația Vitest, să caut pe Google și chiar să întreb ChatGPT, dar nu reușesc să găsesc o explicație pentru care înainte să nu fie rulat Each mai devreme. Poate cineva să mă ajute? Mulțumesc!

Răspuns #1

Se pare că răspunsul a fost foarte simplu. BeforeEach nu era executat pentru că a fost importat accidental din pachetul node:test și nu din pachetul vitest. Aceasta demonstrează că uneori merită să revizuiești lucrurile evidente.