Javascript 当使用多个不可接受的prop值时,为什么我的React propTypes的Jest测试会中断?

Javascript 当使用多个不可接受的prop值时,为什么我的React propTypes的Jest测试会中断?,javascript,unit-testing,reactjs,jestjs,Javascript,Unit Testing,Reactjs,Jestjs,在关于如何使用Jest测试React中的propTypes的文章中,我提出了一个模拟console.error的解决方案,这是其他人以前做过但我认为可以改进的事情。下面是我的解决方案,一个函数。下面的示例代码包含一些任意的proptype,然后进行测试。对于每个测试,我希望可接受值的第一个子数组不会导致React调用console.error的mock,但我希望不可接受值的第二个子数组中的每个测试prop值会导致调用mock testPropTypes函数 示例代码 MyComponent.js

在关于如何使用Jest测试React中的propTypes的文章中,我提出了一个模拟console.error的解决方案,这是其他人以前做过但我认为可以改进的事情。下面是我的解决方案,一个函数。下面的示例代码包含一些任意的proptype,然后进行测试。对于每个测试,我希望可接受值的第一个子数组不会导致React调用console.error的mock,但我希望不可接受值的第二个子数组中的每个测试prop值会导致调用mock

testPropTypes函数

示例代码

MyComponent.js仅支持以下类型:

MyComponent.test.js:

该代码在简单的情况下似乎运行良好。但是,对于更复杂的propType(即myProp2),不可接受的测试prop值的子数组会产生一些有趣的结果。测试道具是可选的还是必需的空值的存在或不存在没有区别,即道具始终表现正常。然而,其他测试道具类型的值似乎以某种神秘的方式相互作用。如果使用单个非空的不可接受测试属性值,测试将按预期执行。但是,如果使用了不止一个这样的非null不可接受测试prop值,则测试将中断,如同第二个、第三个等不可接受类型(null除外)实际上是可接受的一样进行响应

发生了什么事?我如何解决这个问题?

简短回答

简单的回答是,对于至少一些复杂的proptype,比如myProp2,您只能包含任何类型的单个非null测试prop类型。这是该方法的固有限制,并且来自React/Jest的内置特性,该特性导致多个错误条件,否则可能会产生相同的错误消息,只报告一次。事实上,即使对于像myProp1这样的简单PropType,也存在一个相关但稍微隐藏的限制:您可以包含任意数量的不可接受的非空测试prop值,但同一类型的测试prop值不能超过一个。e、 g.[,{},[]可以工作,但是[,'x',{},[]不会注意到有两个字符串

这个限制很重要,因为目前似乎没有更好的方法来测试React中的proptype。然而,这种方法的限制似乎是可以管理的,允许对proptype进行合理的测试

细节

目前,在如何使用这种方法方面存在一些明显的限制,即mocking console.error,如果超过这个步骤,可能会导致一些难以跟踪的测试错误

这些限制的根源是React的行为,或者是React的玩笑?这是内置的错误报告机制。当前它似乎只生成多个console.error消息,至少对于propTypes问题,当这些消息彼此不同时。这背后的基本原理可能是重复的相同消息不会提供新信息,因此此类消息只应第一次显示,这似乎是合理的。然而,这意味着两个不同的错误条件,当它们分别发生时,产生相同的错误消息,当它们发生在同一测试运行中时,只会产生一条错误消息。所有后续相同的消息都将被抑制。这对策略(如这一策略)有几个进一步的影响,这些策略必然期望console.error的输出与测试失败条件精确相关,如下所示

目前,对于示例代码中myProp1这样的简单PropType,对于不同数据类型的所有不可接受的prop值,似乎都会出现不同的错误消息,例如

"Warning: Failed prop type: Invalid prop 'myProp1' of type 'string'
supplied to 'MyComponent', expected 'number'."
但是,相同类型的第二个测试属性值的错误消息似乎已被抑制。因此,所有不同数据类型(如[123、[123]、'123'])的不可接受值数组将导致每个数据类型产生预期的错误消息,从而允许测试工作。但是,不可接受的值数组(其中一些值具有相同的数据类型,例如[123,456,[123],“123]”)将不起作用,因为只有任何特定数据类型的第一个值将产生预期的错误消息,因此任何后续类似类型值的假定错误消息将被抑制,从而破坏测试。因此,对于简单的属性类型,只要它们都是不同的数据类型,就可以使用任意多个不可接受的测试属性值。这似乎不是什么大问题,因为您不需要测试特定数据类型的多个值,但您需要注意这一点

然而,也许更重要的是,一些像myProp2这样的复杂的PropType自相矛盾地导致了更简单的错误消息,例如

"Warning: Failed prop type: Invalid prop 'myProp2' supplied to 'MyComponent'."
这是我 然而,答案是,只有任何数据类型的第一个非null的不可接受测试prop值会生成错误消息,所有后续测试使用其他不可接受值时,其错误消息都会被抑制。因此,对于这样复杂的proptype,不幸的是,对于测试套件中特定组件的特定prop,您只能包含单个不可接受的prop值。这不包括所需复杂propType的null或未定义的值,因为这些值会生成不同的错误消息,因此不会被抑制。但是,请注意,您仍然可以测试任意多个可接受的值,例如,允许您在示例代码中测试myProp2的多个可能可接受的属性值。更令人困惑的是,这种限制不仅存在于测试中,而且存在于测试之间。因此,任何测试套件只能包含针对特定组件的特定道具的单个不可接受道具类型测试。这似乎是一个重大的限制,绝对有必要牢记在心。然而,它实际上可能不像最初听起来那样具有限制性,因为人们通常不会期望对特定组件的特定道具多次测试不可接受的值。因此,这种方法,即mocking console.error,似乎仍然是合理的,而且似乎仍然是测试proptype的唯一真正方法

请注意,此限制可能会在将来发生变化,任何时候,如果响应和/或Jest更改其使用console.error的方式,以响应不适当的prop值

还要注意的是,我还没有研究React/Jest如何为其他复杂的propTypes执行错误报告,例如React.propTypes.arrayOf。反对。大概,为了有力地测试这些,您首先需要调查针对不适当的值生成了什么类型的错误消息,以及如果任何此类消息被抑制,会产生什么类型的错误消息。

简短回答

简单的回答是,对于至少一些复杂的proptype,比如myProp2,您只能包含任何类型的单个非null测试prop类型。这是该方法的固有限制,并且来自React/Jest的内置特性,该特性导致多个错误条件,否则可能会产生相同的错误消息,只报告一次。事实上,即使对于像myProp1这样的简单PropType,也存在一个相关但稍微隐藏的限制:您可以包含任意数量的不可接受的非空测试prop值,但同一类型的测试prop值不能超过一个。e、 g.[,{},[]可以工作,但是[,'x',{},[]不会注意到有两个字符串

这个限制很重要,因为目前似乎没有更好的方法来测试React中的proptype。然而,这种方法的限制似乎是可以管理的,允许对proptype进行合理的测试

细节

目前,在如何使用这种方法方面存在一些明显的限制,即mocking console.error,如果超过这个步骤,可能会导致一些难以跟踪的测试错误

这些限制的根源是React的行为,或者是React的玩笑?这是内置的错误报告机制。当前它似乎只生成多个console.error消息,至少对于propTypes问题,当这些消息彼此不同时。这背后的基本原理可能是重复的相同消息不会提供新信息,因此此类消息只应第一次显示,这似乎是合理的。然而,这意味着两个不同的错误条件,当它们分别发生时,产生相同的错误消息,当它们发生在同一测试运行中时,只会产生一条错误消息。所有后续相同的消息都将被抑制。这对策略(如这一策略)有几个进一步的影响,这些策略必然期望console.error的输出与测试失败条件精确相关,如下所示

目前,对于示例代码中myProp1这样的简单PropType,对于不同数据类型的所有不可接受的prop值,似乎都会出现不同的错误消息,例如

"Warning: Failed prop type: Invalid prop 'myProp1' of type 'string'
supplied to 'MyComponent', expected 'number'."
但是,相同类型的第二个测试属性值的错误消息似乎已被抑制。因此,所有不同数据类型(如[123、[123]、'123'])的不可接受值数组将导致每个数据类型产生预期的错误消息,从而允许测试工作。但是,不可接受的值数组(其中一些值具有相同的数据类型,例如[123,456,[123],“123]”)将不起作用,因为只有任何特定数据类型的第一个值将产生预期的错误消息,因此任何后续类似类型值的假定错误消息将被抑制,从而破坏测试。因此,对于简单的propTypes,您可以使用任意多个不可接受的测试prop值,只要它们都不同 数据类型。这似乎不是什么大问题,因为您不需要测试特定数据类型的多个值,但您需要注意这一点

然而,也许更重要的是,一些像myProp2这样的复杂的PropType自相矛盾地导致了更简单的错误消息,例如

"Warning: Failed prop type: Invalid prop 'myProp2' supplied to 'MyComponent'."
然而,这意味着只有任何数据类型的第一个非null的不可接受测试prop值产生错误消息,所有后续测试使用其他不可接受值时,其错误消息被抑制。因此,对于这样复杂的proptype,不幸的是,对于测试套件中特定组件的特定prop,您只能包含单个不可接受的prop值。这不包括所需复杂propType的null或未定义的值,因为这些值会生成不同的错误消息,因此不会被抑制。但是,请注意,您仍然可以测试任意多个可接受的值,例如,允许您在示例代码中测试myProp2的多个可能可接受的属性值。更令人困惑的是,这种限制不仅存在于测试中,而且存在于测试之间。因此,任何测试套件只能包含针对特定组件的特定道具的单个不可接受道具类型测试。这似乎是一个重大的限制,绝对有必要牢记在心。然而,它实际上可能不像最初听起来那样具有限制性,因为人们通常不会期望对特定组件的特定道具多次测试不可接受的值。因此,这种方法,即mocking console.error,似乎仍然是合理的,而且似乎仍然是测试proptype的唯一真正方法

请注意,此限制可能会在将来发生变化,任何时候,如果响应和/或Jest更改其使用console.error的方式,以响应不适当的prop值


还要注意的是,我还没有研究React/Jest如何为其他复杂的propTypes执行错误报告,例如React.propTypes.arrayOf。反对。大概,为了有力地测试这些错误,您首先需要调查针对不适当的值生成了什么类型的错误消息,以及如果任何此类消息被抑制,会产生什么类型的错误消息。

这里有一点关于原因的详细信息,以扩展Andrew Willems对导致问题的条件的详细描述

请注意,我在这里的解释适用于Facebook最近从React中提取的道具类型库,特别是在使用其导出的checkPropTypes函数检查道具时

模块以名为loggedTypeFailures的对象的形式保持可变状态:

展示如何使用它,并解释其想法:

      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使用相同消息失败时,loggedTypeFailures检查中的error.message将成功并跳过日志记录

由于loggedTypeFailures是模块专用的,因此无法访问或重置它


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

这里有一些关于为什么的详细信息,以扩展Andrew Willems对导致问题的条件的详细描述

请注意,我在这里的解释适用于Facebook最近从React中提取的道具类型库,特别是在使用其导出的checkPropTypes函数检查道具时

模块以名为loggedTypeFailures的对象的形式保持可变状态:

展示如何使用它,并解释其想法:

      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使用相同消息失败时,loggedTypeFailures检查中的error.message将成功并跳过日志记录

由于loggedTypeFailures是模块专用的,因此无法访问或重置它

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

      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;