首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么我的Jest测试在使用多个不可接受的道具值时会破坏propTypes?

为什么我的Jest测试在使用多个不可接受的道具值时会破坏propTypes?
EN

Stack Overflow用户
提问于 2017-01-29 02:15:18
回答 2查看 3.3K关注 0票数 1

对一个不同的问题的回答中,关于如何使用Jest测试propTypes,我提出了一种模拟console.error的解决方案,这是其他人以前做过的,但我认为可以改进。我的解决方案,一个函数,在下面。下面的示例代码包含一些任意的propTypes,然后进行测试。对于每个测试,我预期“可接受值”的第一个子数组不会导致console.error的模拟被React调用,但我希望“不可接受值”的第二个子数组中的每个测试支柱值都会被调用。

testPropTypes 函数

代码语言:javascript
运行
复制
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);
};

示例代码

MyComponent.js (只有propTypes__):

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
describe('MyComponent', () => {

    it('should accept an optional number for myProp1', () => {
        const testValues = [
            [0, null],                   // acceptable values
            ['', {}, [], Symbol(), true] // 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
            ['', {}, [], Symbol(), true, null] // unacceptable values
        ];
        testPropTypes(MyComponent, 'myProp2', testValues);
    });
});

在简单的情况下,代码似乎工作得很好。然而,对于更复杂的propType (即myProp2 )来说,不可接受的测试道具值的子数组产生了一些有趣的结果。null的存在与否(它测试道具是可选的还是必需的)并没有什么区别,也就是说,它始终运行正常。然而,其他测试道具类型的值似乎以某种神秘的方式相互作用。如果使用一个非null不可接受的测试支柱值,则测试将按预期执行。但是,如果使用了多个这样的非null不可接受的测试支柱值,则测试中断,好像第二、第三等不可接受的类型(同样不是null)是可接受的。

怎么回事,我怎么解决这个问题?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-01-29 02:15:18

短答案

简单的回答是,至少对于一些复杂的propTypes,比如对于myProp2,您只能包含任何类型的单个非null测试支柱类型。这是这种方法的固有限制,来自React/Jest的内置特性,它会导致多个错误条件,否则可能只报告一次相同的错误消息。事实上,即使对于简单的propTypes (如myProp1 ),也存在一个相关但稍微隐藏的限制:您可以包含任意数量不同的不可接受的非null测试道具值,但不能超过同一类型中的一种。例如,['', {}, []]将工作,但['', 'x', {}, []]不能工作(请注意,有两个字符串)。

这个限制很重要,因为目前似乎没有更好的方法来测试propTypes的反应。但是,这种方法所受的限制似乎是可以管理的,这使得propTypes仍然可以进行合理的测试。

详细信息

目前,在如何使用这种方法(即模仿console.error)方面有一些重要的限制,如果超越了这一限制,可能是一些难以跟踪的测试错误的来源。

这些限制的根源是一种反应行为(或者可能是开玩笑来回应反应?)这是内置的它的错误报告机制。目前,它似乎只产生多个console.error消息,至少对于propTypes问题,当这些消息彼此不同时。这背后的理由大概是重复相同的消息没有提供新的信息,因此这样的消息应该只在第一次显示,这似乎是合理的。但是,这意味着两个不同的错误条件,当它们单独发生时,产生相同的错误消息时,只会在相同的测试运行中产生一个错误消息。所有后续相同的消息都被抑制。这对于类似这样的策略有几个进一步的含义,这些策略必然期望console.error的输出与测试失败的条件完全相关,如下所示。

目前,对于简单的propTypes (如示例代码中的myProp1 ),不同数据类型的所有不可接受的支柱值似乎都会出现不同的错误消息。

代码语言:javascript
运行
复制
"Warning: Failed prop type: Invalid prop 'myProp1' of type 'string'
supplied to 'MyComponent', expected 'number'."

但是,同一类型的第二个测试支柱值的错误消息似乎被抑制了。因此,一个不可接受的值数组--所有不同的数据类型(如[123, [123], '123'] )--都将使每个数据类型产生一条预期的错误消息,从而允许测试正常工作。但是,一些数据类型相同的值(如[123, 456, [123], '123'] )的数组将无法工作,因为只有任何特定数据类型的第一个值才会产生预期的错误消息,因此任何后续的类似类型值的假定错误消息都将被抑制,从而破坏测试。因此,对于简单的propTypes,只要它们都是不同的数据类型,就可以使用任意多个不可接受的测试支柱值。这似乎不是一个大问题,因为无论如何您不应该测试一个特定数据类型的多个值,但是您需要注意这个警告。

然而,也许更重要的是,一些复杂的propTypes (比如myProp2)自相矛盾地(虽然可能是合理的)导致了更简化的错误消息。

代码语言:javascript
运行
复制
"Warning: Failed prop type: Invalid prop 'myProp2' supplied to 'MyComponent'."

但是,这意味着只有任何数据类型的第一个非null不可接受的测试支柱值才会产生错误消息,所有后续的测试都使用其他不可接受的值来抑制它们的错误消息。因此,使用如此复杂的propTypes,不幸的是,您只能在测试套件中为特定组件包含一个不可接受的道具值。这不包括所需复杂nullundefinedpropType值,因为这些值会产生一个独特的错误消息,因此不会被抑制。但是,请注意,您仍然可以测试任意多个可接受的值,例如,允许您在示例代码中测试多个可接受的myProp2道具值。更令人困惑的是,这一限制不仅存在于测试中,还存在于测试之间。因此,任何测试套件都只能包含针对特定组件的特定支柱的单个不可接受的支柱类型测试。这似乎是一个很大的限制,绝对有必要记住这一点。然而,它实际上可能并不像最初听起来那样受到限制,因为人们通常不会期望为某个特定组件多次测试不可接受的值。因此,这种方法,即模拟console.error,仍然是合理的,而且仍然是测试propTypes的唯一真正方法。

请注意,这个限制在将来可能会改变,任何时候反应和/或Jest都会改变它如何使用console.error来响应不适当的道具值。

还请注意,我还没有详细研究React/Jest如何为其他复杂的propTypes (例如React.PropTypes.arrayOf... .objectOf... .shape等)执行错误报告。想必,要测试这些错误消息,您首先需要研究为不适当的值生成什么样的错误消息,以及哪些错误消息被抑制。

票数 5
EN

Stack Overflow用户

发布于 2017-07-06 20:35:56

这里有一些细节,为什么,扩大安德鲁威利斯的详细描述,造成问题的条件。

请注意,我在这里的解释适用于Facebook最近提取的prop-types库,特别是当使用它导出的checkPropTypes函数检查道具时。

checkPropTypes.js模块以名为loggedTypeFailures on 第16行的对象的形式保持可变状态。

代码语言:javascript
运行
复制
 var loggedTypeFailures = {};

第47-50行展示了它是如何使用的,并解释了这种想法:

代码语言:javascript
运行
复制
      if (error instanceof Error && !(error.message in loggedTypeFailures)) {
        // Only monitor this failure once because there tends to be a lot of the
        // same error.
        loggedTypeFailures[error.message] = true;

当propType检查失败时,在记录错误之前将消息设置为loggedTypeFailures上的一个键,以跟踪它已被记录。下一次当一个propType在相同的消息中失败时,error.message in loggedTypeFailures检查成功,并跳过日志记录。

由于loggedTypeFailures对模块是私有的,因此无法访问或重置它。

如果您需要以更健壮的方式检测propType故障(例如,用于单元测试),我发布了一个帮助您的小型库:检查支柱类型。它以与checkPropTypes.js相同的方式检查道具,但返回错误而不是记录错误。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41916992

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档