包含 Context.Provider 和 Context.Consumer 的 React Context 测试组件

Posted

技术标签:

【中文标题】包含 Context.Provider 和 Context.Consumer 的 React Context 测试组件【英文标题】:React Context test component that contains both Context.Provider and Context.Consumer 【发布时间】:2020-07-19 22:02:08 【问题描述】:

我正在尝试测试一个同时包含 React Context 的提供者和消费者的组件。请参阅下面的 App.tsx。 提供者在一个也处理状态的包装类中。

如何模拟 ConfigurationContextProvider 包装类,以便它可以正确地向消费者提供 loadedStatus 值以测试 App.tsx 的各种呈现?

App.tsx:

    public render(): React.ReactNode 
    return (
      <ConfigurationContextProvider>
        <ConfigurationContext.Consumer>
           ( 
            loadedStatus,
          ): ReactElement => 
            switch( loadedStatus )
              case ConfigStatus.GoodConfiguration:
                return ( this.renderApp());
              case ConfigStatus.NotLoaded:
                return ( this.renderUnloadedApp() );
              case ConfigStatus.BadConfiguration:
                return ( this.renderBadConfiguration() );
            
          
        </ConfigurationContext.Consumer> 
      </ConfigurationContextProvider>
    )

ContextProvider.tsx:

    export const ConfigurationContext = React.createContext<ConfigurationState | undefined>(undefined);

    export enum ConfigStatus 
      NotLoaded,
      BadConfiguration,
      GoodConfiguration
    

    export interface ConfigurationState
      loadedStatus: ConfigStatus;
      baseUrl: URL;
    

    export class ConfigurationContextProvider extends Component< , ConfigurationState> 

      constructor( props:  )
        super(props);
        this.state =  
          loadedStatus: ConfigStatus.NotLoaded,
          baseUrl: null
        
      

      async componentDidMount(): Promise<void> 
        await this.loadConfiguration();
      

      setConfiguration(configuration: Response): void 
        const URL_KEY = 'BASE_URL';
        const URL_VALUE = configuration[URL_KEY];
        if (!Object.prototype.hasOwnProperty.call(configuration, URL_KEY))
        
          this.setState( loadedStatus: ConfigStatus.BadConfiguration );
          console.error(`Missing field in configs: $ URL_KEY `);
        
        else 
          try
            this.setState(
              loadedStatus: ConfigStatus.GoodConfiguration, 
              baseUrl: new URL(URL_VALUE),
            );
          
          catch 
            this.setState( loadedStatus: ConfigStatus.BadConfiguration );
            console.error(`Bad URL in environment configs - $ URL_KEY  : $ URL_VALUE `);
          
        
      

      loadConfiguration(): Promise<void> 
        const configPromise: Promise<Response> = fetch('./env.json');
        return configPromise.then( (response) => response.json())
          .then( (config: Response) => this.setConfiguration(config));
      

      render(): React.ReactNode 
        return (
          <ConfigurationContext.Provider value =   
            ...this.state,
           >
            this.props.children
          </ConfigurationContext.Provider>
        )
      
    

【问题讨论】:

【参考方案1】:

发现最简单的方法是监视 ConfigurationContextProvider 的 loadConfig 函数,并根据需要将响应设置为 setConfig。这种方式可以测试各种结果并根据需要进行完整的上下文/状态更新。

jest.spyOn(
  ConfigurationContextProvider.prototype, 'loadConfiguration').mockImplementation(
    function(this: ConfigurationContextProvider) 
      this.setConfiguration(  BASE_URL: 'http://localhost:8080' );
      return Promise.resolve();
    
);

【讨论】:

以上是关于包含 Context.Provider 和 Context.Consumer 的 React Context 测试组件的主要内容,如果未能解决你的问题,请参考以下文章

为啥第二个(兄弟)React Context Provider 不起作用?或者,如果上面有同级 Context Provider,为啥 React Context Provider 不起作用

是否可以在多个组件上使用相同的 <Context.Provider> ?

如何访问反应Context.Provider值中的另一个函数?

除了 value 道具之外的任何道具都可以在 Context.Provider 中工作吗

如何创建一个`context.Provider` /`context.Consumer`-like结构来传递bot应用程序中的值?

React Context Provider 所有子级重新渲染