使用Jest模拟Es6类

Spi*_*Xel 19 javascript unit-testing node.js ecmascript-6 jestjs

我正在尝试使用接收参数的构造函数来模拟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');
Run Code Online (Sandbox Code Playgroud)

很明显,我试图模拟Socket和类函数连接的方式是错误的,但我找不到正确的方法.

请在您的回答中解释您为嘲笑这个以及为什么每个都是必要的逻辑步骤+如果可能的话,提供外部链接到Jest官方文档!

谢谢您的帮助!

sto*_*one 24

更新:

所有这些信息以及更多内容现已添加到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中传递的相同参数.

一个例子值得千言万语,所以这里是代码.此外,还有一个回购展示了所有这些,在这里: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
    };
  });
Run Code Online (Sandbox Code Playgroud)

(注意,在你的代码中:Socket.mock.calls[0][1]应该是[0][0],[0][2]应该是[0][1].)

接下来,一个人为的例子

内联一些解释.

mocked-class.js.注意,在测试期间从不调用此代码.

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

  mockedMethod() {
    console.log('Called mockedMethod');
  }
}
Run Code Online (Sandbox Code Playgroud)

mocked-class-consumer.js.此类使用模拟类创建对象.我们希望它创建一个模拟版本而不是真实的东西.

import MockedClass from './mocked-class';

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

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
});
Run Code Online (Sandbox Code Playgroud)