首頁 >web前端 >js教程 >用 Jest 和 typescript 進行嘲笑 - 備忘錄

用 Jest 和 typescript 進行嘲笑 - 備忘錄

Barbara Streisand
Barbara Streisand原創
2024-12-22 22:31:11589瀏覽

Mocking with Jest and typescript - a cheatsheet

Jest 非常擅長在 javascript/typescript 中模擬導入,但我發現很難記住實作細節。

函數和物件需要以不同的方式進行模擬,預設匯出的模擬與命名匯出略有不同,且 Jest 與打字稿的配合效果不佳。將所有這些事情結合起來,可能很難找出甚至搜尋適合您的模擬場景的正確方法。

我建立本指南是為了回答這個問題「如何模擬我的導入?」無論導入是什麼。預設或命名、函數或物件。

我的環境

我已經使用以下版本的軟體測試了所有這些方法:

  • 節點 v22.11.0
  • 笑話 v29.7.0
  • ts-jest v29.2.5
  • @types/jest v29.5.14

使用預設的最小 jest.config.js 檔案:

export default {
  testEnvironment: 'node',
  transform: {
    '^.+.tsx?$': ['ts-jest', {}],
  },
  testMatch: ['**/*.test.ts'],
};

模擬導入

廣泛常見的導入分為我們可能想要模擬的兩類:

  • 功能
  • 物體

我們將從函數開始依序解決這兩個問題。

導入函數

從模組導出的函數可以命名或預設。我們會看看兩者。第一:

模擬模組中的命名導出函數

這應該用於模擬模組中的命名導出函數,如下所示:

// ./path/to/module.ts

export function doSomething(...) {
  ...
}

可以這樣嘲笑:

import { doSomething } from './path/to/module';

// note: This should be the path to the module from the test file,
// NOT from the module that contains the doSomething function itself.
jest.mock('./path/to/module', () => ({
  doSomething: jest.fn(),
}));

...

it('should do something', () => {
  // We need to assert that the function is a jest.Mock
  // so that typescript will allow us to call mock methods.
  (doSomething as jest.Mock).mockReturnValue(mockValue);

  // run your test here

  expect(doSomething).toHaveBeenCalledTimes(1); // etc.
});

模擬從模組返回的預設函數

這應該用於模擬模組預設導出的函數,如下所示:

// ./path/to/module.ts

export default function doSomething(...) {
  ...
}

它的模擬方式與命名導出類似:

import doSomething from './path/to/module'

jest.mock('./path/to/module', () => ({
  __esModule: true,
  default: jest.fn()
}))

...

it('should do something', () => {
  (doSomething as jest.Mock).mockResolvedValue(mockData);

  // Run your test here

  expect(doSomething).toHaveBeenCalledTimes(5);
});

導入對象

模擬導出的物件(可以是類別、json 物件或其他物件)時需要考慮一些變化。

  • 它是命名導出還是預設導出?
  • 它有沒有我們也希望模擬的方法,或只是屬性?

沒有方法的模擬預設對象

如果您只需要模擬屬性(例如設定檔),而不是方法,那麼具體做法如下:

import config from '../config';

jest.mock('../config', () => ({
  __esModule: true,
  default: {
    apiKey: '123MockKey',
    ...
  },
}));

...

it('Should do something', () => {
  ...
});

如果每次檢定需要改變模擬屬性:

import config from '../config';

const mockConfig = {
  apiKey: '123MockKey',
  ...
};

jest.mock('../config', () => ({
  __esModule: true,
  default: mockConfig,
}));

...

beforeEach(() => {
  // restore defaults before each test
  mockConfig.apiKey = '123MockKey';
  ...
});

it('Should do something', () => {
  mockConfig.apiKey = 'new value';

  // rest of the test
});

// more tests

模擬沒有方法的命名導出對象

與模擬預設導出物件非常相似:

import { config } from '../config';

const mockConfig = {
  apiKey: '123MockKey',
  ...
};

jest.mock('../config', () => ({
  config: mockConfig,
}));

// the rest is exactly the same as when mocking a default export object.

使用方法模擬對象

當從模組導出(命名或預設)帶有方法的物件時,我們需要模擬這些方法的輸出,方法略有不同。

上課:

// ./path/to/module.ts

class ComplicatedThing {
  // properties, fields, constructor etc. go here

  getData() {
    ...
  }

  ...
}

// note: I don't necessarily recommend exporting an instance
// of a class like this - purely illustrative for testing purposes.
// https://medium.com/@lazlojuly/are-node-js-modules-singletons-764ae97519af
export const complicatedThing = new ComplicatedThing(...);

並模擬我們匯出的物件:

export default {
  testEnvironment: 'node',
  transform: {
    '^.+.tsx?$': ['ts-jest', {}],
  },
  testMatch: ['**/*.test.ts'],
};

模擬預設導出物件是完全相同的,除了我們定義模擬時:

// ./path/to/module.ts

export function doSomething(...) {
  ...
}

獎勵:模擬物件上的方法作為參數直接傳遞給測試函數/類

這是為了模擬一個對象,該對像不是直接導入到您正在測試的模組中,而是作為參數傳遞給類別/函數。

注意:如果您正在模擬一個類,您可能想要建立一個介面並建立該介面的模擬實作以傳遞到您的函數/類中。這將使您無需進行如下不優雅的類型斷言惡作劇。

import { doSomething } from './path/to/module';

// note: This should be the path to the module from the test file,
// NOT from the module that contains the doSomething function itself.
jest.mock('./path/to/module', () => ({
  doSomething: jest.fn(),
}));

...

it('should do something', () => {
  // We need to assert that the function is a jest.Mock
  // so that typescript will allow us to call mock methods.
  (doSomething as jest.Mock).mockReturnValue(mockValue);

  // run your test here

  expect(doSomething).toHaveBeenCalledTimes(1); // etc.
});
// ./path/to/module.ts

export default function doSomething(...) {
  ...
}
import doSomething from './path/to/module'

jest.mock('./path/to/module', () => ({
  __esModule: true,
  default: jest.fn()
}))

...

it('should do something', () => {
  (doSomething as jest.Mock).mockResolvedValue(mockData);

  // Run your test here

  expect(doSomething).toHaveBeenCalledTimes(5);
});

結論

我希望這對你有用,也對我未來的自己有用,當我下次努力記住如何在打字稿中模擬導入的細節時。

我希望它能滿足您所有簡單的模擬需求,並為您在模擬更複雜的導入時提供一個起點。

感謝您的閱讀。

以上是用 Jest 和 typescript 進行嘲笑 - 備忘錄的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn