skip to Main Content

struggling to write some unit tests for a files who using redux/sagas.

app.tsx file looks like this

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 file

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

and test file 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()
  })
})

when trying to run test file Im getting following error:

 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.

Can someone help it? I’m pretty new testing redux/sagas and unfortunatelly left alone in a team.

In tests taking injectStore store from same place where taking in app.tsx file
If needed this is configureStore function:

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
}

2

Answers


  1. Chosen as BEST ANSWER

    found the issue. It was because

    export const injectStore = (_store: any) => {
      store = _store
    }
    

    is in same file together with a axiosInterceptor and axiosRequest.

    Once I move in a different file, and use that file to import in app.tsx like:

    // 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')
        )
    
    

    problem is solved


  2. and in webapi-axios-interceptor.tsx imported store from a app.tsx

    import { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
    import { store } from  '../app'
    import { Header } from '../constants'
    
    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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search