reactjs 如何在redux工具包中模拟存储

jhkqcmku  于 2023-01-08  发布在  React
关注(0)|答案(4)|浏览(141)
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { render, screen, fireEvent } from '@testing-library/react';
import MyApp from './MyApp ';

const initialState = {};
const mockStore = configureStore(initialState);

describe('<MyApp />', () => {
  it('click button and shows modal', () => {
    render(
      <Provider store={mockStore}>
        <MyApp />
      </Provider>
    );

    fireEvent.click(screen.getByText('ADD MIOU'));
    expect(queryByText('Add MIOU Setting')).toBeInTheDocument();
  });
});

我正在使用jest和redux工具包与reactjs,并试图模拟一个商店写一个测试。但得到以下错误

    • 类型错误:store.getState不是函数**

有什么办法能弥补吗?我错过什么了吗?

n9vozmp4

n9vozmp41#

我假设您正在尝试测试一个连接的组件,并且您希望(1)运行操作创建器和简化器,以及(2)作为测试的一部分更新redux状态。
我没有使用过redux-mock-store,但是我在their documentation上看到了下面的注解,这让我相信这个库可能不会像您期望的那样工作:
请注意,这个库被设计用来测试与操作相关的逻辑,而不是与reducer相关的逻辑。换句话说,它不会更新Redux存储。
我建议您尝试使用这种方法测试连接组件,我已经使用这种方法编写了更新redux状态和呈现连接组件的测试。
首先,重写RTL render方法:

// test-utils.js
import React from 'react'
import { render as rtlRender } from '@testing-library/react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
// Import your own reducer
import reducer from '../reducer'

function render(
  ui,
  {
    initialState,
    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 }

然后直接引用新的render方法而不是RTL,还可以为测试提供初始状态。

import React from 'react'
// We're using our own custom render function and not RTL's render
// our custom utils also re-export everything from RTL
// so we can import fireEvent and screen here as well
import { render, fireEvent, screen } from '../../test-utils'
import App from '../../containers/App'

it('Renders the connected app with initialState', () => {
  render(<App />, { initialState: { user: 'Redux User' } })

  expect(screen.getByText(/redux user/i)).toBeInTheDocument()
})

(All从redux.js.org复制的代码)

kqqjbcuj

kqqjbcuj2#

我遇到了和你一样的问题,但是感谢@srk链接Redux文档和React测试库文档,我找到了一个非常好的解决方案,适合我使用TypeScript:

// store.ts - just for better understanding
export const store = configureStore({
  reducer: { user: userReducer },
})

export type RootState = ReturnType<typeof store.getState>
// test-utils.ts
import React, { ReactElement } from 'react'
import { Provider } from 'react-redux'
import { render as rtlRender, RenderOptions } from '@testing-library/react'
import {
  configureStore,
  EmptyObject,
  EnhancedStore,
  PreloadedState,
} from '@reduxjs/toolkit'

// import your reducers
import userReducer from 'features/user/user.slice'

import type { RootState } from 'app/store'

// ReducerTypes is just a grouping of each slice type,
// in this example i'm just passing down a User Reducer/State.
// With this, you can define the type for your store.
// The type of a configureStore() is called EnhancedStore,
// which in turn receives the store state as a generic (the same from store.getState()).
type ReducerTypes = Pick<RootState, 'user'>
type TStore = EnhancedStore<ReducerTypes>

type CustomRenderOptions = {
  preloadedState?: PreloadedState<ReducerTypes & EmptyObject>
  store?: TStore
} & Omit<RenderOptions, 'wrapper'>

function render(ui: ReactElement, options?: CustomRenderOptions) {
  const { preloadedState } = options || {}

  const store =
    options?.store ||
    configureStore({
      reducer: {
        user: userReducer,
      },
      preloadedState,
    })

  function Wrapper({ children }: { children: React.ReactNode }) {
    return <Provider store={store}>{children}</Provider>
  }

  return rtlRender(ui, { wrapper: Wrapper, ...options })
}

// re-export everything
export * from '@testing-library/react'
// override render method
export { render }

然后,您只需要将带有preloadedState属性的对象作为第二个参数传递给您的呈现器;如果你想的话,你甚至可以用"store"属性定义一个新的商店。

describe('[Component] Home', () => {
  it('User not logged', () => {
    const component = render(<Home />)
    expect(component.getByText(/User is: undefined/i)).toBeInTheDocument()
  })

  it('User logged in', () => {
    const component = render(<Home />, {
      preloadedState: { user: { name: 'John' /* ...other user stuff */ } },
    })
    expect(component.getByText(/User is: John/i)).toBeInTheDocument()
  })
})
vatpfxk5

vatpfxk53#

我找不到其他地方来粘贴我关于redux工具包和redux模拟存储的发现。

  • 为了调度异步thunk和测试结果,您可以在创建模拟存储时指定调度类型。*
import configureStore from 'redux-mock-store';
import { getDefaultMiddleware } from '@reduxjs/toolkit'

const middlewares = getDefaultMiddleware();
const mockStore = createMockStore<IRootState, AppDispatch>(middlewares);

describe('my thunk action', () => {
  const store = mockStore();

  test('calls my service', async() => {
    await store.dispatch(myThunk({ id: 32 }));

    expect(myService).toBeCalledWith({ id: 32 });
  });

  test('contains foo bar actions', async() => {
    await store.dispatch(myThunk({ id: 32 }));

    expect(store.getActions()).toEqual(....);
  });
});
1tu0hz3e

1tu0hz3e4#

自2023年1月起不再建议在redux、see the docs herethis answer for more information中模拟商店。

相关问题