为 MockComponent 制作 JSX 语法并使用 typescript 键入它

Nor*_*ldt 6 javascript typescript reactjs jestjs react-native

想知道是否有人对如何解决这个问题有一些好的建议。得到了这个测试助手实用程序,我添加了一些类型:

import { jest } from '@jest/globals'
import React from 'react'

// https://learn.reactnativeschool.com/courses/781007/lectures/14173979
export function mockComponent(moduleName: string, propOverrideFn = (props: Record<string, any>) => ({})) {
  const RealComponent = jest.requireActual(moduleName) as React.ComponentType<any>
  const CustomizedComponent = (props: Record<string, any>) => {
    return React.createElement(
      'CustomizedComponent',
      {
        ...props,
        ...propOverrideFn(props),
      },
      props.children
    )
  }
  CustomizedComponent.propTypes = RealComponent.propTypes

  return CustomizedComponent
}
Run Code Online (Sandbox Code Playgroud)

所以目前我可以这样称呼它

jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
  return mockComponent('react-native/Libraries/Components/Touchable/TouchableOpacity', (props) => {
    return {
      onPress: props.disabled ? () => {} : props.onPress
    }
  })
})
Run Code Online (Sandbox Code Playgroud)

但我希望能够更像这样称呼它


jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
  return <MockComponent 
          module='TouchableOpacity' 
          onPress={props => props.disabled ? () => {} : props.onPress}
         />
})

Run Code Online (Sandbox Code Playgroud)

或者

jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
 return <MockComponent 
          module='TouchableOpacity'
          propOverride={props => ({onPress: props.disabled ? () => {} : props.onPress, ...props})}
        />
})
Run Code Online (Sandbox Code Playgroud)

Vin*_* La 5

如果您查看没有 JSX 的 React,您会发现受 XML 启发的语法 ( <MockComponent />) 只是 的缩写React.createElement('MockComponent')

现在,如果您重命名mockComponentMockComponent并尝试使用尖括号语法,第一个问题是您的函数接收两个参数。React 组件要么是采用一个构造函数参数(props)的类组件,要么是采用一个参数(同样是 props)的函数组件。第二问题是,当您的函数需要返回渲染的React元素时,它会返回 React 功能组件。

解决这个问题的一种方法是转换mockComponent为 React 功能组件以及FC 的makemodule和props。propOverride

// https://learn.reactnativeschool.com/courses/781007/lectures/14173979
export function MockComponent(props) {
  const { moduleName, propOverrideFn, ...customComponentProps } = props;

  const RealComponent = jest.requireActual(moduleName) as React.ComponentType<any>
  const CustomizedComponent = (props: Record<string, any>) => {
    return React.createElement(
      'CustomizedComponent',
      {
        ...props,
        ...propOverrideFn(props),
      },
      props.children
    )
  }
  CustomizedComponent.propTypes = RealComponent.propTypes

  return <CustomizedComponent {...customComponentProps} />
}
Run Code Online (Sandbox Code Playgroud)

差异很微妙但很重要。这里我修改MockComponent为采用单一prop参数以与React.createElement(). 这导致了如何区分用于 的 propsCustomizedComponent和用于 的参数的问题mockComponent()。在这里,我使用 JavaScript 解构和扩展运算符将modulepropOverride与 的 props分开CustomizedComponent

最后,我使用JSX 扩展语法将用于的任意属性传递到CustomizedComponentinto CustomizedComponent,并使用尖括号来渲染它(而不是返回函数)。

我将留下一个练习,让您为 MockComponent 的 props 提供适当的 TypeScript 定义。module您可以简单地将其定义为 Record<string, any> 和and的并集propOverride。但是,您可以使用模板定义,and和的 props 的MockComponent<Toolbar>并集也是如此。modulepropOverrideToolbar

哦,我差点忘了。你的 Jest 电话看起来像

jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
    (props) => {
        return <MockComponent 
            module='TouchableOpacity' 
            onPress={props => props.disabled ? () => {} : props.onPress}
            {...props}
        />
   }
})
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢 Vincent 的整个解释和代码建议 (3认同)