简体   繁体   English

如何在 React 测试库中设置 InitialState

[英]How to set InitialState in React Testing Library

I am writing a test where I need to render a component, but the rendering of my component is not working and I am receiving this error:我正在编写一个需要渲染组件的测试,但我的组件的渲染不起作用,我收到此错误:

Uncaught [TypeError: Cannot read property 'role' of undefined].

This is because in the componentDidMount function in my component I am checking if this.props.authentication.user.role === 'EXPERT' .这是因为在我的组件中的componentDidMount function 中,我正在检查this.props.authentication.user.role === 'EXPERT' However, this.props.authentication has user as undefined .但是, this.props.authenticationuserundefined

This is the correct initialState for my program, but for the test I want to set my initialState to have a user object.这是我的程序的正确初始状态,但对于测试,我想将我的initialState状态设置为拥有user initialState That is why I redefine initialState in my test.这就是我在测试中重新定义initialState的原因。 However, the component does not render with that new initialState .但是,组件不会使用新的initialState进行渲染。

Here is the testing file:这是测试文件:

import { Component }  from '../Component.js';
import React from 'react';
import { MemoryRouter, Router } from 'react-router-dom';
import { render, cleanup, waitFor } from '../../test-utils.js';
import '@testing-library/jest-dom/extend-expect';

afterEach(cleanup)

describe('Component Testing', () => {
  test('Loading text appears', async () => { 
    const { getByTestId } = render(
      <MemoryRouter><Component /></MemoryRouter>,
      {
        initialState: {
          authentication: {
            user: { role: "MEMBER", memberID:'1234' }
          }
        }
      },
    );       
    let label = getByTestId('loading-text')
    expect(label).toBeTruthy()
  })
});

Here is the Component file:这是组件文件:

class Component extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      tasks: [],
      loading: true,
    }

    this.loadTasks = this.loadTasks.bind(this)
  }

  componentDidMount() {
    if (
      this.props.authentication.user.role == 'EXPERT' ||
      this.props.authentication.user.role == 'ADMIN'
    ) {
       this.loadTasks(this.props.location.state.member)
    } else {
       this.loadTasks(this.props.authentication.user.memberID)
    }
  }

  mapState(state) {
    const { tasks } = state.tasks
    return {
      tasks: state.tasks,
      authentication: state.authentication
    }
  }
}

I am also using a custom render function that is below我还使用了自定义渲染 function 如下

import React from 'react' 
import { render as rtlRender } from '@testing-library/react' 
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import { initialState as reducerInitialState, reducer } from './_reducers'
import rootReducer from './_reducers'
import configureStore from './ConfigureStore.js';
import { createMemoryHistory } from 'history'

function render(ui, {
    initialState = reducerInitialState,
    store = configureStore({}),
    ...renderOptions
  } = {}
) {
  function Wrapper({ children }) {
    return <Provider store={store}>{children}</Provider>
  }
  return rtlRender(ui, { wrapper: Wrapper, ...renderOptions })
}

// re-export everything
export * from '@testing-library/react'

// override render method
export { render }

Perhaps I am coming super late to the party but maybe this can serve to someone.也许我来晚了,但也许这对某人有用。 What I have done for a Typescript setup is the following (all this is within test-utils.tsx )我为 Typescript 设置所做的工作如下(所有这些都在test-utils.tsx内)

const AllProviders = ({
  children,
  initialState,
}: {
  children: React.ReactNode
  initialState?: RootState
}) => {
  return (
    <ThemeProvider>
      <Provider store={generateStoreWithInitialState(initialState || {})}>
        <FlagsProvider value={flags}>
          <Router>
            <Route
              render={({ location }) => {
                return (
                  <HeaderContextProvider>
                    {React.cloneElement(children as React.ReactElement, {
                      location,
                    })}
                  </HeaderContextProvider>
                )
              }}
            />
          </Router>
        </FlagsProvider>
      </Provider>
    </ThemeProvider>
  )
}

interface CustomRenderProps extends RenderOptions {
  initialState?: RootState
}

const customRender = (
  ui: React.ReactElement,
  customRenderProps: CustomRenderProps = {}
) => {
  const { initialState, ...renderProps } = customRenderProps

  return render(ui, {
    wrapper: (props) => (
      <AllProviders initialState={initialState}>{props.children}</AllProviders>
    ),
    ...renderProps,
  })
}
export * from '@testing-library/react'

export { customRender as render }

Worth to mention that you can/should remove the providers that doesn't make any sense for your case (like probably the FlagsProvider or the HeaderContextProvider ) but I leave to illustrate I decided to keep UI providers within the route and the others outside (but this is me making not much sense anyway)值得一提的是,您可以/应该删除对您的情况没有任何意义的提供程序(可能是FlagsProviderHeaderContextProvider ,但我留下来说明我决定将 UI 提供程序保留在路由内,将其他提供程序保留在外部(但是无论如何,这对我来说没有多大意义)

In terms of the store file I did this:商店文件而言,我这样做了:

//...omitting extra stuff

const storeConfig = {
   // All your store setup some TS infer types may be a extra challenge to solve
}

export const store = configureStore(storeConfig)

export const generateStoreWithInitialState = (initialState: Partial<RootState>) =>
  configureStore({ ...storeConfig, preloadedState: initialState })

//...omitting extra stuff

Cheers!干杯!

I am not sure what you are doing in the configure store, but I suppose the initial state of your component should be passed in the store.我不确定您在配置商店中在做什么,但我想您的组件的初始 state 应该在商店中传递。

import React from 'react' 
 import { render as rtlRender } from '@testing-library/react' 
 import { createStore } from 'redux'
 import { Provider } from 'react-redux'
 import { initialState as reducerInitialState, reducer } from './_reducers'
 import { createMemoryHistory } from 'history'
    
function render(
  ui,
  {
    initialState = reducerInitialState,
    store = createStore(reducer,initialState),
    ...renderOptions
  } = {}
) {
  function Wrapper({ children }) {
    return <Provider store={store}>{children}</Provider>
  }
  return rtlRender(ui, { wrapper: Wrapper, ...renderOptions })
}

// re-export everything
export * from '@testing-library/react'

// override render method
export { render }

I hope it will help you:)我希望它会帮助你:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM