如何通过Jest测试React PropTypes?

时间:2014-09-30 15:45:33

标签: reactjs jestjs reactjs-testutils react-proptypes

我正在为我的React代码编写Jest测试,并希望使用/测试PropType检查。我对Javascript世界很陌生。我使用npm安装react-0.11.2并且有一个简单的:

var React = require('react/addons');

在我的测试中。我的测试看起来非常类似于jest / react教程示例,代码如下:

var eventCell = TestUtils.renderIntoDocument(
  <EventCell
    slot={slot}
    weekId={weekId}
    day={day}
    eventTypes={eventTypes}
    />
);

var time = TestUtils.findRenderedDOMComponentWithClass(eventCell, 'time');
expect(time.getDOMNode().textContent).toEqual('19:00 ');

然而,EventCell组件中的PropType检查似乎未被触发。我知道检查只在开发模式下运行,但后来我也认为通过npm获取react给了你开发版本。当我使用watchify构建组件时,我的浏览器会触发检查。

我错过了什么?

6 个答案:

答案 0 :(得分:32)

潜在问题是How to test console.log?

简短的回答是,您应该在测试期间替换console.{method}。常见的方法是使用spies。在这种特殊情况下,您可能希望使用stubs来阻止输出。

以下是使用Sinon.js的示例实现(Sinon.js提供了独立的间谍,存根和模拟):

import {
    expect
} from 'chai';
import DateName from './../../src/app/components/DateName';
import createComponent from './create-component';
import sinon from 'sinon';

describe('DateName', () => {
    it('throws an error if date input does not represent 12:00:00 AM UTC', () => {
        let stub;

        stub = sinon.stub(console, 'error');

        createComponent(DateName, {date: 1470009600000});

        expect(stub.calledOnce).to.equal(true);
        expect(stub.calledWithExactly('Warning: Failed propType: Date unix timestamp must represent 00:00:00 (HH:mm:ss) time.')).to.equal(true);

        console.error.restore();
    });
});

在此示例中,DataName组件在使用不代表精确日期(12:00:00 AM)的时间戳值初始化时将引发错误。

我正在使用console.error方法(这是Facebook warning模块在​​内部用于生成错误的方法)。我确保已经调用了一次存根,并且只有一个参数表示错误。

答案 1 :(得分:7)

<强>简介

@ Gajus的答案肯定帮助了我(所以,感谢Gajus )。但是,我想我会提供一个答案:

  • 使用更强最新的React (v15.4.1)
  • 使用 Jest (React附带)
  • 允许为单个道具
  • 测试多个道具值
  • 更通用

<强>摘要

与Gajus和其他人在其他地方建议的方法一样,我建议的基本方法也是确定React是否使用console.error来响应不可接受的测试道具值即可。具体而言,此方法涉及对每个测试支柱值执行以下操作:

  • 嘲笑和清除console.error (以确保事先致电console.error不会干扰),
  • 使用正在考虑的测试支柱值创建组件,
  • 确认console.error是否按预期被解雇

testPropTypes功能

以下代码可以放在测试中,也可以作为单独的导入/必需模块/文件放置:

const testPropTypes = (component, propName, arraysOfTestValues, otherProps) => {
    console.error = jest.fn();
    const _test = (testValues, expectError) => {
        for (let propValue of testValues) {
            console.error.mockClear();
            React.createElement(component, {...otherProps, [propName]: propValue});
            expect(console.error).toHaveBeenCalledTimes(expectError ? 1 : 0);
        }
    };
    _test(arraysOfTestValues[0], false);
    _test(arraysOfTestValues[1], true);
};

调用函数

任何检查propTypes的测试都可以使用三个或四个参数调用testPropTypes

  • component,由道具修改的React 组件;
  • propName,正在测试的名称;
  • arraysOfTestValues,要测试的道具的所有所需测试值的数组数组:
    • 第一个子数组包含所有可接受的测试道具值,而
    • 第二个子数组包含所有不可接受的测试道具值;和
  • 可选地,otherProps,一个包含道具名称/值对的对象,用于此组件的任何其他所需道具

    需要使用otherProps对象来确保React不会对console.error进行无关的调用,因为其他必需的道具无意中丢失了。只需为任何所需道具包含一个可接受的值,例如: {requiredPropName1: anyAcceptableValue, requiredPropName2: anyAcceptableValue}

功能逻辑

该功能执行以下操作:

  • 设置了console.error 的模拟,这是React用于报告不正确类型道具的模拟。

  • 对于测试道具值的每个子阵列,提供遍历每个子阵列中的每个测试道具值以测试道具类型:

    • 两个子阵列中的第一个应该是可接受的测试道具值的列表。
    • 第二个应该是不可接受的测试道具值
  • 在每个单独测试道具值的循环中,首先清除console.error模拟,以便可以假定检测到的任何错误消息来自此测试。< / p>

  • 然后使用测试道具值以及当前未测试的任何其他必要道具创建组件的实例。

  • 最后,检查是否查看是否已触发警告,如果您的测试尝试使用不合适或缺失的道具创建组件,则会发生这种情况。

测试可选与必需的道具

请注意,从React的角度来看,将null(或undefined)分配给道具值基本上与不为该道具提供任何价值相同。根据定义,这对于可选的道具是​​可接受的,但对于所需的道具是不可接受的。因此,null置于可接受或不可接受的值数组中,分别测试该支柱是可选的还是必需的

示例代码

MyComponent.js(只是propTypes):

MyComponent.propTypes = {
    myProp1: React.PropTypes.number,      // optional number
    myProp2: React.PropTypes.oneOfType([  // required number or array of numbers
        React.PropTypes.number,
        React.PropTypes.arrayOf(React.PropTypes.number)
    ]).isRequired

MyComponent.test.js:

describe('MyComponent', () => {

    it('should accept an optional number for myProp1', () => {
        const testValues = [
            [0, null],   // acceptable values; note: null is acceptable
            ['', []] // unacceptable values
        ];
        testPropTypes(MyComponent, 'myProp1', testValues, {myProp2: 123});
    });

    it('should require a number or an array of numbers for myProp2', () => {
        const testValues = [
            [0, [0]], // acceptable values
            ['', null] // unacceptable values; note: null is unacceptable
        ];
        testPropTypes(MyComponent, 'myProp2', testValues);
    });
});

此方法的局限性(重要)

目前,如何使用这种方法存在一些重大限制,如果过度使用,可能会成为一些难以追踪的测试错误的来源。 this other SO question/answer解释了这些限制的原因和影响。总之,对于简单的道具类型,例如myProp1,只要它们是所​​有不同的数据类型,您就可以根据需要测试任意数量不可接受的非null测试道具值 EM>。对于某些复杂道具类型,例如myProp2,您只能测试任何类型的不可接受的非null道具值 。请参阅其他问题/答案以进行更深入的讨论。

答案 2 :(得分:5)

模拟console.error不适合在单元测试中使用! @AndrewWillems在上面的评论中链接到another SO question,描述了这种方法的问题。

查看this issue on facebook/prop-types有关该库抛出而不是记录propType错误的能力的讨论(在撰写本文时,它不受支持)。

我发布了一个帮助程序库,以平均时间check-prop-types提供该行为。您可以像这样使用它:

import PropTypes from 'prop-types';
import checkPropTypes from 'check-prop-types';

const HelloComponent = ({ name }) => (
  <h1>Hi, {name}</h1>
);

HelloComponent.propTypes = {
  name: PropTypes.string.isRequired,
};

let result = checkPropTypes(HelloComponent.propTypes, { name: 'Julia' }, 'prop', HelloComponent.name);
assert(`result` === null);

result = checkPropTypes(HelloComponent.propTypes, { name: 123 }, 'prop', HelloComponent.name);
assert(`result` === 'Failed prop type: Invalid prop `name` of type `number` supplied to `HelloComponent`, expected `string`.');

答案 3 :(得分:3)

新软件包jest-prop-type-error易于添加,并因PropType错误而失败:

通过以下方式安装:

yarn add -D jest-prop-type-error

然后在package.json部分的setupFiles的{​​{1}}中添加以下内容:

jest

答案 4 :(得分:1)

由于ReactJS只会向控制台发送警告但实际上不会抛出错误,我会用这种方式测试prop值:

var myTestElement = TestUtils.renderIntoDocument(
<MyTestElement height={100} /> );

it("check MyTestElement props", function() {

   expect( typeof myTestElement.props.height ).toEqual ( 'number' );

});

答案 5 :(得分:0)

对于基于Jest的单元测试,在setup.js(prop-type错误)或console.error(反应兼容问题,如仍使用{ {1}})最终被称为:

console.warn

当任何测试调用componentWillUpdate时中断了-对我们来说,调用beforeEach(() => { jest.spyOn(console, 'error') jest.spyOn(console, 'warn') }) afterEach(() => { /* eslint-disable no-console,jest/no-standalone-expect */ expect(console.error).not.toBeCalled() expect(console.warn).not.toBeCalled() }) jest.clearAllMocks()`有所帮助。

这还要求您的应用不要调用“ {errors”(错误引号,因为通常不是一个好主意)来jest.restoreAllMocks()