簡體   English   中英

使用 Jest 模擬 Es6 類

[英]Mock Es6 classes using Jest

我正在嘗試使用接收參數的構造函數來模擬 ES6 類,然后在該類上模擬不同的類函數以繼續使用 Jest 進行測試。

問題是我找不到有關如何解決此問題的任何文檔。 我已經看過這篇文章,但它沒有解決我的問題,因為實際上 OP 甚至不需要模擬課程! 該帖子中的另一個答案也根本沒有詳細說明,沒有指向任何在線文檔,也不會導致可復制的知識,因為它只是一個代碼塊。

所以說我有以下課程:

//socket.js;

module.exports = class Socket extends EventEmitter {

    constructor(id, password) {
        super();

        this.id = id;
        this.password = password;

        this.state = constants.socket.INITIALIZING;
    }

    connect() {
        // Well this connects and so on...
    } 

};

//__tests__/socket.js

jest.mock('./../socket');
const Socket = require('./../socket');
const socket = new Socket(1, 'password');

expect(Socket).toHaveBeenCalledTimes(1);

socket.connect()
expect(Socket.mock.calls[0][1]).toBe(1);
expect(Socket.mock.calls[0][2]).toBe('password');

很明顯,我試圖模擬Socket連接它的類函數的方式是錯誤的,但我找不到正確的方法。

請在您的回答中解釋您為模擬這一點而采取的邏輯步驟以及為什么每個步驟都是必要的 + 如果可能,請提供指向 Jest 官方文檔的外部鏈接!

謝謝您的幫助!

更新:

所有這些信息以及更多信息現在都已添加到 Jest 文檔的新指南“ ES6 Class Mocks”中

完全披露:我寫的。 :-)


模擬 ES6 類的關鍵是知道ES6 類是一個函數 因此,模擬也必須是一個函數

  1. 調用jest.mock('./mocked-class.js'); ,並導入'./mocked-class.js'。
  2. 對於要跟蹤調用的任何類方法,創建一個指向模擬函數的變量,如下所示: const mockedMethod = jest.fn(); . 在下一步中使用它們。
  3. 調用MockedClass.mockImplementation() 傳入一個箭頭函數,該函數返回一個包含任何模擬方法的對象,每個方法都設置為自己的模擬函數(在步驟 2 中創建)。
  4. 同樣的事情可以使用手動模擬(__mocks__ 文件夾)來模擬 ES6 類。 在這種情況下,導出的模擬是通過調用jest.fn().mockImplementation()來創建的,與上面 (3) 中描述的參數相同。 這將創建一個模擬函數。 在這種情況下,您還需要導出要監視的任何模擬方法。
  5. 同樣的事情可以通過調用jest.mock('mocked-class.js', factoryFunction) ,其中 factoryFunction 再次與上面 3 和 4 中傳遞的參數相同。

一個例子值一千字,所以這是代碼。 此外,這里有一個 repo 演示了所有這些: https : //github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working

首先,對於您的代碼

如果您要添加以下設置代碼,您的測試應該會通過:

const connectMock = jest.fn(); // Lets you check if `connect()` was called, if you want

Socket.mockImplementation(() => {
    return {
      connect: connectMock
    };
  });

(注意,在您的代碼中: Socket.mock.calls[0][1]應該是[0][0] ,而[0][2]應該是[0][1] 。)

接下來,一個人為的例子

內嵌一些解釋。

模擬類.js 請注意,在測試期間永遠不會調用此代碼。

export default class MockedClass {
  constructor() {
    console.log('Constructed');
  }

  mockedMethod() {
    console.log('Called mockedMethod');
  }
}

mocked-class-consumer.js 這個類使用模擬類創建一個對象。 我們希望它創建一個模擬版本而不是真實版本。

import MockedClass from './mocked-class';

export default class MockedClassConsumer {
  constructor() {
    this.mockedClassInstance = new MockedClass('yo');
    this.mockedClassInstance.mockedMethod('bro');
  }
}

mocked-class-consumer.test.js - 測試:

import MockedClassConsumer from './mocked-class-consumer';
import MockedClass from './mocked-class';

jest.mock('./mocked-class'); // Mocks the function that creates the class; replaces it with a function that returns undefined.

// console.log(MockedClass()); // logs 'undefined'

let mockedClassConsumer;
const mockedMethodImpl = jest.fn();

beforeAll(() => {
  MockedClass.mockImplementation(() => {
    // Replace the class-creation method with this mock version.
    return {
      mockedMethod: mockedMethodImpl // Populate the method with a reference to a mock created with jest.fn().
    };
  });
});

beforeEach(() => {
  MockedClass.mockClear();
  mockedMethodImpl.mockClear();
});

it('The MockedClassConsumer instance can be created', () => {
  const mockedClassConsumer = new MockedClassConsumer();
  // console.log(MockedClass()); // logs a jest-created object with a mockedMethod: property, because the mockImplementation has been set now.
  expect(mockedClassConsumer).toBeTruthy();
});

it('We can check if the consumer called the class constructor', () => {
  expect(MockedClass).not.toHaveBeenCalled(); // Ensure our mockClear() is clearing out previous calls to the constructor
  const mockedClassConsumer = new MockedClassConsumer();
  expect(MockedClass).toHaveBeenCalled(); // Constructor has been called
  expect(MockedClass.mock.calls[0][0]).toEqual('yo'); // ... with the string 'yo'
});

it('We can check if the consumer called a method on the class instance', () => {
  const mockedClassConsumer = new MockedClassConsumer();
  expect(mockedMethodImpl).toHaveBeenCalledWith('bro'); 
// Checking for method call using the stored reference to the mock function
// It would be nice if there were a way to do this directly from MockedClass.mock
});

對我來說,這種用模擬類替換真實類是有效的。

// Content of real.test.ts

jest.mock("../RealClass", () => {
  const mockedModule = jest.requireActual(
    "../test/__mocks__/RealClass"
  );
  return {
    ...mockedModule,
  };
});

var codeTest = require("../real");
  it("test-real", async () => {
    let result = await codeTest.handler();
    expect(result).toMatch(/mocked.thing/);
  });

// Content of real.ts
import {RealClass} from "../RealClass";
export const handler = {
   let rc = new RealClass({doing:'something'});
   return rc.realMethod("myWord");
}
// Content of ../RealClass.ts
export class RealClass {
  constructor(something: string) {}
  async realMethod(input:string) {
    return "The.real.deal "+input;
  }
// Content of ../test/__mocks__/RealClass.ts
export class RealClass {
  constructor(something: string) {}
  async realMethod(input:string) {
    return "mocked.thing "+input;
  }

對不起,如果我拼錯了一些東西,但我正在即時寫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM