如何通过Jest测试React PropTypes?

qkf9rpyu  于 2023-02-20  发布在  Jest
关注(0)|答案(8)|浏览(160)

我正在为我的React代码编写Jest测试,希望能使用/test 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构建组件时,这些检查在我的浏览器中触发。
我错过了什么?

ua4mk5z4

ua4mk5z41#

添加新的包jest-prop-type-error很简单,但在PropType错误时失败:
安装方式:

yarn add -D jest-prop-type-error

然后将以下内容添加到jest部分中的package.jsonsetupFiles

"setupFiles": [
  "jest-prop-type-error"
]
qnzebej0

qnzebej02#

由于ReactJS只会向控制台发送警告,而不会真正抛出错误,因此我用这种方法测试prop值:

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

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

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

});
3npbholx

3npbholx3#

对于基于Jest的单元测试,在您的setup.js上使用此代码将导致console.error(prop类型错误)或console.warn(React compat问题,如仍在使用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.restoreAllMocks()时,这个问题就会中断--对我们来说,调用`` jest.clearAllMocks()'反而有帮助。
它还要求您的应用不要调用console.errorconsole.warn进行“错误处理”(请小心引用,因为这通常不是一个好主意)。

vhmi4jdf

vhmi4jdf4#

这就是我如何使用Sinon在React中测试PropType错误的。另外,要检查每个丢失原型的错误,请尝试console.log(sinon.assert.notCalled(console.error))

import { expect } from 'chai';
import DateName from './../../src/app/components/DateName';
import sinon from 'sinon';
    
describe('DateName', () => {

  function renderComponent(date) {
    return render(
        <DateName date={date} />
    );
  }

  it('throws an error if date input does not represent 12:00:00 AM UTC', () => {
          let stub;
          stub = sinon.stub(console, 'error');
          renderComponent(undefined);
          expect(stub.calledOnce).toEqual(true);
          sinon.assert.calledWithMatch(console.error,'Warning: Failed %s type: %s%s', 'prop', 'The prop `date` is marked as required in `DateName`, but its value is `undefined`.');
          console.error.restore();
 });
});
xcitsw88

xcitsw885#

使用React测试库https://reactjs.org/docs/testing-recipes.html
Dog.js

import PropTypes from 'prop-types';

export default class Dog {
    constructor(breed, dob, weight) {
        this.breed = breed;
        this.dob = dob;
        this.weight = weight;
    }
}

Dog.propTypes = {
    breed: PropTypes.string,
    dob: PropTypes.instanceOf(Date),
    weight: PropTypes.number,
};

Dog.spec.js

import PropTypes from 'prop-types';
import Dog from './Dog';

describe('Dog.js tests', () => {
    const mockData = {
        breed: 'Shiba Inu',
        dob: new Date('March 14, 2017 08:30:00'),
        weight: 21,
    }

    const instance = {
        name: 'MockDogComponent',
        model: new Dog(mockData),
    };

    beforeEach(() => {
        const { breed, dob, weight } = mockData;
        instance.model = new Dog(breed, dob, weight);
    });

    it('should have valid propTypes', () => {        
        Object.keys(instance.model).forEach((prop) => 
            expect(PropTypes
                .checkPropTypes(Dog.propTypes, instance.model, prop, instance.name))
                .toBeUndefined()
            );
    });
pzfprimi

pzfprimi6#

根本问题是How to test console.log ?
简短的回答是,您应该在测试期间替换console.{method},常用的方法是使用spies,在这种特殊情况下,您可能希望使用stubs来防止输出。
下面是一个使用Sinon.js的示例实现(Sinon.js提供独立的spies、stub和mock):

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模块内部用来生成错误的方法),我确保存根已经被调用过一次,并且只有一个参数代表错误。

nhjlsmyf

nhjlsmyf7#

    • 简介**

@Gajus的回答确实帮了我的忙(所以,谢谢Gajus)。然而,我想我会提供一个答案:

  • 使用更最新的React(v15.4.1)
  • 使用Jest(随React提供)
  • 允许测试单个 prop 的**多个 prop 值
      • 更通用**
    • 摘要**

就像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,由 prop 修改的React组件;
  • propName,被测 prop 的字符串名称;
  • arraysOfTestValues,要测试的 prop 的所有期望测试值的阵列的阵列:
  • 第一子阵列包含所有可接受的测试适当值,而
  • 第二子阵列包含所有的不可接受的测试真值;以及
  • otherProps(可选),该对象包含此组件的任何其他必需属性属性名称/值对。

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

    • 功能逻辑**

该函数执行以下操作:

  • 它**建立了一个console.error**的模拟,React用它来报告不正确类型的 prop 。
  • 对于测试属性值的每个子数组,只要它循环通过每个子数组中的每个测试属性值以测试属性类型:
  • 两个子数组中的第一个应该是可接受的测试属性值的列表。
  • 第二个应为不可接受的测试属性值
  • 在每个单独测试属性值的循环中,首先清除console.error模拟,以便可以假定检测到的任何错误消息都来自此测试。
  • 然后,使用测试 prop 值以及当前未测试的任何其他必需 prop 来创建组件的示例。
  • 最后,检查是否触发了警告,如果您的测试试图使用不适当的或缺失的属性创建组件,则会触发警告。
    • 可选与必需 prop 的测试**

请注意,将null赋值为(或undefined)到一个prop值,从React的Angular 来看,本质上与不为该prop提供任何值是一样的。根据定义,这对于可选prop是可接受的,但对于必需prop是不可接受的。因此,* * 通过将null放置在可接受或不可接受值的数组中,可以分别测试该属性是可选的还是必需的**。

    • 示例代码**
  • 我的组件. 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
  • 我的组件.测试. 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中解释了这些限制的原因和含义。总之,对于简单的prop类型,如myProp1,您可以测试任意多个不可接受的非null测试prop值 *,只要它们都是不同的数据类型 *。对于某些复杂的prop类型,如myProp2,您只能测试任何类型 * 的 * 单个 * 不可接受的非null prop值 *。有关更深入的讨论,请参阅其他问题/答案。

llew8vvj

llew8vvj8#

模拟console.error不适合在单元测试中使用!@AndrewWillems链接到上面注解中的另一个SO问题,该注解描述了这种方法的问题。
查看this issue on facebook/prop-types,了解该库抛出而不是记录propType错误的能力(在编写本文时,还不支持该功能)。
我已经发布了一个helper库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`.');

相关问题