努力为使用 redux/sagas 的文件编写一些单元测试。
app.tsx
文件看起来像这样
import React, { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { Router } from 'react-router-dom'
import App from './App'
import { injectStore } from './network/webapi-axios-interceptor'
import { history } from './helpers/history'
import './index.css'
import * as serviceWorker from './serviceWorker'
import configureStore from './store'
export const store = configureStore({})
injectStore(store)
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<StrictMode>
<App />
</StrictMode>
</Router>
</Provider>,
document.getElementById('root')
)
webapi-axios-interceptor.tsx
文件
import { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
import { Header } from '../constants'
let store: any
export const injectStore = (_store: any) => {
store = _store
}
function axiosInterceptor(instance: AxiosInstance): any {
return instance.interceptors.request.use(
(request) => axiosRequest(request),
(error) => axiosRequestError(error?.message, error)
)
}
function axiosRequest(
request: InternalAxiosRequestConfig
): InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig> {
if(request.headers)
request.headers = store.getState().config.config.currentUser
return request
}
function axiosRequestError(
message: string,
error?: any
): Promise<any> {
return Promise.reject(error || message)
}
export default axiosInterceptor
和测试文件
tab.test.tsx
:
import React from 'react'
import { render } from '@testing-library/react'
import { mock } from '../__mock__/mock'
import Tab from '../Tab'
import configureStore from '../../../../../store'
import { Provider } from 'react-redux';
import { injectStore } from '../../../../../network/webapi-axios-interceptor'
describe('Tab', () => {
beforeEach(() => {
jest.resetAllMocks();
})
it('should render "Tab" component', () => {
const store = configureStore({})
injectStore(store)
const result = render(
<Provider store={store}>
<Tab
key={mock.key}
onSuccessfulMerge={jest.fn()}
/>
</Provider>
)
expect(result).toBeInTheDocument()
})
})
尝试运行测试文件时出现以下错误:
FAIL src/pages/Customer/containers/Tab/__tests__/tab.test.tsx
● Test suite failed to run
TypeError: (0 , _webapiAxiosInterceptor.injectStore) is not a function
12 |
13 | export const store = configureStore({})
> 14 | injectStore(store)
| ^
15 |
16 | ReactDOM.render(
17 | <Provider store={store}>
at Object.<anonymous> (src/index.tsx:14:12)
at Object.<anonymous> (src/configuration/forms/options/standard-rejection-reasons.ts:1:2)
at Object.<anonymous> (src/configuration/forms/fields.ts:4:1)
at Object.<anonymous> (src/configuration/forms/configuration.ts:2:1)
at Object.<anonymous> (src/pages/Customer/containers/Form/Form.tsx:16:1)
at Object.<anonymous> (src/pages/Customer/containers/Tab/Tab.tsx:43:1)
at Object.<anonymous> (src/pages/Customer/containers/Tab/__tests__/tab.test.tsx:4:1)
at TestScheduler.scheduleTests (node_modules/react-scripts/node_modules/@jest/core/build/TestScheduler.js:333:13)
at runJest (node_modules/react-scripts/node_modules/@jest/core/build/runJest.js:404:19)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 8.732 s
Ran all test suites related to changed files.
有人可以帮忙吗?我是刚测试 redux/sagas 的新手,不幸的是我独自留在团队中。
在测试中,从接收 app.tsx 文件的同一位置存储
injectStore
如果需要,这是configureStore函数:
import { createStore, Store, compose, applyMiddleware } from 'redux'
import createSagaMiddleware, { END } from 'redux-saga'
import { createEpicMiddleware } from 'redux-observable'
import { rootReducer } from './rootReducer'
import { rootSaga } from './rootSaga'
import { rootEpic } from './rootEpic'
import { configFetchStarted } from './actions/configActions'
interface IExtendedStore extends Store {
runSaga: any
runEpic: any
close: () => void
}
export default function configureStore(initialState: any) {
const sagaMiddleware = createSagaMiddleware()
const epicMiddleware = createEpicMiddleware()
const composeEnhancers =
typeof window === 'object' &&
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose
const enhancer = composeEnhancers(
applyMiddleware(sagaMiddleware, epicMiddleware)
)
const store = createStore(
rootReducer(),
initialState,
enhancer
) as IExtendedStore
store.runSaga = sagaMiddleware.run
store.runEpic = epicMiddleware.run
store.close = () => store.dispatch(END as any)
store.runSaga(rootSaga)
store.runEpic(rootEpic)
store.dispatch(configFetchStarted())
return store
}
发现问题了。 原来是因为
export const injectStore = (_store: any) => {
store = _store
}
与
axiosInterceptor
和 axiosRequest
位于同一文件中。
一旦我移入不同的文件,并使用该文件导入
app.tsx
,如下所示:
// inject-store.ts
let store: any
const injectStore = (_store: any) => {
store = _store
}
export default injectStore
// app.tsx
import React, { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { Router } from 'react-router-dom'
import App from './App'
import { injectStore } from './network/inject-store' // separate file from `webapi-axios-interceptor`
import { history } from './helpers/history'
import './index.css'
import * as serviceWorker from './serviceWorker'
import configureStore from './store'
export const store = configureStore({})
injectStore(store)
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<StrictMode>
<App />
</StrictMode>
</Router>
</Provider>,
document.getElementById('root')
)
问题解决了