介绍
@Gajus 的回答绝对帮助了我(所以,谢谢 Gajus)。但是,我想我会提供一个答案:
- 使用更新的 React (v15.4.1)
- 使用Jest(React 自带)
- 允许为单个道具测试多个道具值
- 更通用
概括
就像 Gajus 和其他人在这里建议的方法一样,我建议的基本方法也是确定 React 是否console.error
使用它来响应不可接受的 test prop value。具体来说,这种方法涉及对每个测试道具值执行以下操作:
- 模拟和清除
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
,被测道具的字符串名称;
arraysOfTestValues
,要测试的道具
的所有所需测试值的数组数组:
- 第一个子数组包含所有可接受的测试道具值,而
- 第二个子数组包含所有不可接受的测试道具值;和
可选,otherProps
一个对象,其中包含该组件的任何其他必需道具的道具名称/值对。
需要该otherProps
对象来确保 React 不会console.error
因为其他必需的 props 无意中丢失而进行不相关的调用。只需为任何所需的道具包含一个可接受的值,例如{requiredPropName1: anyAcceptableValue, requiredPropName2: anyAcceptableValue}
.
功能逻辑
该函数执行以下操作:
它设置了一个模拟,console.error
React 使用该模拟来报告不正确类型的道具。
对于每个测试道具值的子数组,它会遍历每个子数组中的每个测试道具值以测试道具类型:
- 两个子数组中的第一个应该是可接受的测试道具值列表。
- 第二个应该是不可接受的测试道具值。
在每个单独的测试道具值的循环中,首先console.error
清除模拟,以便可以假定检测到的任何错误消息来自该测试。
然后使用测试道具值以及当前未测试的任何其他必要的必需道具创建组件的实例。
最后,检查是否已触发警告,如果您的测试尝试使用不适当或缺少的道具创建组件,则应该发生这种情况。
测试可选道具和必需道具
请注意,从 React 的角度来看,将null
(or undefined
) 分配给 prop 值与不为该 prop 提供任何值本质上是一样的。根据定义,这对于可选道具是可以接受的,但对于必需道具是不可接受的。因此,通过将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);
});
});
这种方法的局限性(重要)
目前,如何使用这种方法存在一些重大限制,如果过度使用,可能会导致一些难以追踪的测试错误。这些限制的原因和影响在另一个 SO question/answer中进行了解释。总之,对于简单的 prop 类型,例如 for myProp1
,您可以根据需要测试尽可能多的不可接受的非null
测试 prop 值,只要它们都是不同的数据类型。对于某些复杂的 prop 类型,例如 for myProp2
,您只能测试任何类型的单个不可接受的非null
prop 值。请参阅其他问题/答案以进行更深入的讨论。