javascript 使用 Jest 模拟 Es6 类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47410939/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 07:21:38  来源:igfitidea点击:

Mock Es6 classes using Jest

javascriptnode.jsunit-testingecmascript-6jestjs

提问by SpiXel

I'm trying to mock an ES6 class with a constructor that receives parameters, and then mock different class functions on the class to continue with testing, using Jest.

我正在尝试使用接收参数的构造函数来模拟 ES6 类,然后使用 Jest 在该类上模拟不同的类函数以继续测试。

Problem is I can't find any documents on how to approach this problem. I've already seen this post, but it doesn't resolve my problem, because the OP in fact didn't even need to mock the class! The other answer in that post also doesn't elaborate at all, doesn't point to any documentation online and will not lead to reproduceable knowledge, since it's just a block of code.

问题是我找不到有关如何解决此问题的任何文档。我已经看过这篇文章,但它没有解决我的问题,因为实际上 OP 甚至不需要模拟课程!该帖子中的另一个答案也根本没有详细说明,没有指向任何在线文档,也不会导致可复制的知识,因为它只是一个代码块。

So say I have the following class:

所以说我有以下课程:

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

As obvious, the way I'm trying to mock Socketand the class function connecton it is wrong, but I can't find the right way to do so.

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

Please explain, in your answer, the logical steps you make to mock this and why each of them is necessary + provide external links to Jest official docs if possible!

请在您的回答中解释您为模拟这一点而采取的逻辑步骤以及为什么每个步骤都是必要的 + 如果可能,请提供指向 Jest 官方文档的外部链接!

Thanks for the help!

谢谢您的帮助!

回答by stone

Update:

更新:

All this info and more has now been added to the Jest docs in a new guide, "ES6 Class Mocks."

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

Full disclosure: I wrote it. :-)

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



The key to mocking ES6 classes is knowing that an ES6 class is a function. Therefore, the mock must also be a function.

模拟 ES6 类的关键是知道ES6 类是一个函数。因此,模拟也必须是一个函数

  1. Call jest.mock('./mocked-class.js');, and also import './mocked-class.js'.
  2. For any class methods you want to track calls to, create a variable that points to a mock function, like this: const mockedMethod = jest.fn();. Use those in the next step.
  3. Call MockedClass.mockImplementation(). Pass in an arrow function that returns an object containing any mocked methods, each set to its own mock function (created in step 2).
  4. The same thing can be done using manual mocks (__mocks__ folder) to mock ES6 classes. In this case, the exported mock is created by calling jest.fn().mockImplementation(), with the same argument described in (3) above. This creates a mock function. In this case, you'll also need to export any mocked methods you want to spy on.
  5. The same thing can be done by calling jest.mock('mocked-class.js', factoryFunction), where factoryFunction is again the same argument passed in 3 and 4 above.
  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 中传递的相同参数。

An example is worth a thousand words, so here's the code. Also, there's a repo demonstrating all of this, here: https://github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working

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

First, for your code

首先,对于您的代码

if you were to add the following setup code, your tests should pass:

如果您要添加以下设置代码,您的测试应该会通过:

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

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

(Note, in your code: Socket.mock.calls[0][1]should be [0][0], and [0][2]should be [0][1]. )

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

Next, a contrived example

接下来,一个人为的例子

with some explanation inline.

内嵌一些解释。

mocked-class.js. Note, this code is never called during the test.

模拟类.js。请注意,在测试期间永远不会调用此代码。

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

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

mocked-class-consumer.js. This class creates an object using the mocked class. We want it to create a mocked version instead of the real thing.

模拟类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- the test:

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