Jest:mocking console.error - 测试失败

Mat*_*tis 9 javascript reactjs jestjs enzyme

问题:

我有一个简单的React组件,我用它来学习用Jest和Enzyme测试组件.当我使用道具时,我添加了prop-types模块来检查开发中的属性. prop-types用于console.error在未传递强制道具或道具是错误数据类型时发出警报.

当我传递丢失/错误输入的道具时,我想模拟console.error计算它被调用的次数prop-types.

使用这个简化的示例组件和测试,我希望这两个测试表现如下:

  1. 使用0/2所需道具的第一个测试应该捕获模拟调用两次.
  2. 使用1/2必需道具的第二次测试应该捕获一次调用的模拟.

相反,我明白了:

  1. 第一次测试成功运行.
  2. 第二次测试失败,抱怨模拟函数被调用为零次.
  3. 如果我交换测试的顺序,第一个工作,第二个工作失败.
  4. 如果我将每个测试拆分成单个文件,则两者都有效.
  5. console.error 输出被抑制,所以很明显它被两个人嘲笑.

我确定我错过了一些明显的东西,比如清除模拟错误或其他什么.

当我对导出函数的模块使用相同的结构时,调用console.error一些任意次数,就可以了.

当我用酶/反应测试时,我在第一次测试后撞到了这堵墙.

示例App.js:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export default class App extends Component {

  render(){
    return(
      <div>Hello world.</div>
    );
  }
};

App.propTypes = {
  id : PropTypes.string.isRequired,
  data : PropTypes.object.isRequired
};
Run Code Online (Sandbox Code Playgroud)

示例App.test.js

import React from 'react';
import { mount } from 'enzyme';
import App from './App';

console.error = jest.fn();

beforeEach(() => {
  console.error.mockClear();
});

it('component logs two errors when no props are passed', () => {
  const wrapper = mount(<App />);
  expect(console.error).toHaveBeenCalledTimes(2);
});

it('component logs one error when only id is passed', () => {
  const wrapper = mount(<App id="stringofstuff"/>);
  expect(console.error).toHaveBeenCalledTimes(1);
});
Run Code Online (Sandbox Code Playgroud)

最后的注意事项:是的,当道具丢失时,最好编写组件以生成一些用户友好的输出,然后测试它.但是一旦我发现了这种行为,我就想弄明白我做错了什么,以此来提高我的理解力.显然,我错过了一些东西.

lfe*_*445 10

我遇到了类似的问题,只需要缓存原始方法

const original = console.error

beforeEach(() => {
  console.error = jest.fn()
  console.error('you cant see me')
})

afterEach(() => {
  console.log('log still works')
  console.error('you cant see me')
  console.error = original
  console.error('now you can')
})
Run Code Online (Sandbox Code Playgroud)

  • 您不需要缓存原始方法。你可以只做`console.error.mockRestore()` (6认同)
  • 在这种情况下,mockRestore不起作用。从jest 24文档中:“请注意,仅当使用jest.spyOn创建模拟时,mockFn.mockRestore才起作用。因此,在手动分配jest.fn()时,您必须自己进行恢复。” (2认同)

Mik*_*ier 7

鉴于@DLyman解释的行为,您可以这样做:

describe('desc', () => {
    let spy = spyConsole();

    it('x', () => {
        // [...]
    });

    it('y', () => {
        // [...]
    });

    it('throws [...]', () => {
        shallow(<App />);
        expect(console.error).toHaveBeenCalled();
        expect(spy.console.mock.calls[0][0]).toContain('The prop `id` is marked as required');
    });
});

function spyConsole() {
    // https://github.com/facebook/react/issues/7047
    let spy = {};

    beforeAll(() => {
        spy.console = jest.spyOn(console, 'error').mockImplementation(() => {});
    });

    afterAll(() => {
        spy.console.mockRestore();
    });

    return spy;
}
Run Code Online (Sandbox Code Playgroud)


小智 6

你没有错过任何东西。有一个关于丢失错误/警告消息的已知问题 ( https://github.com/facebook/react/issues/7047 )。

如果你切换你的测试用例('...当只有 id 被传递' - 第一个,'...当没有道具被传递' - 第二个)并console.log('mockedError', console.error.mock.calls);在你的测试用例中添加这样的 内容,你可以看到,消息在第二次测试中不会触发关于缺少 id 的信息。


Pap*_*api 5

上面写的是正确的。我遇到了类似的问题,这是我的解决方案。在对模拟对象进行断言时,它还考虑了情况:

beforeAll(() => {
    // Create a spy on console (console.log in this case) and provide some mocked implementation
    // In mocking global objects it's usually better than simple `jest.fn()`
    // because you can `unmock` it in clean way doing `mockRestore` 
    jest.spyOn(console, 'log').mockImplementation(() => {});
  });
afterAll(() => {
    // Restore mock after all tests are done, so it won't affect other test suites
    console.log.mockRestore();
  });
afterEach(() => {
    // Clear mock (all calls etc) after each test. 
    // It's needed when you're using console somewhere in the tests so you have clean mock each time
    console.log.mockClear();
  });
Run Code Online (Sandbox Code Playgroud)