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();
});
});
I am using jest and redux toolkit with reactjs, and trying to mock a store to write a test.
But got the following error
TypeError: store.getState is not a function
Is there any way to fix this? Am I missing something?
I assume you are trying to test a connected component, and you expect (1) action creators and reducers to be run and (2) redux state to be updated as part of your test?
I have not used redux-mock-store, but I see the following note on their documentation, which leads me to believe this library may not work the way you expect:
Please note that this library is designed to test the action-related logic, not the reducer-related one. In other words, it does not update the Redux store.
I suggest you try this approach for testing connected components. I have used this approach to write tests that update redux state and render connected components.
First, you override the RTL render method:
// 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 }
Then you reference that new render method instead of RTL directly. You can also provide initial state for your test.
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 code copied from redux.js.org.)
I was having the same problem as you but thanks to #srk for linking the Redux docs and, the React Testing Library docs, I found a pretty good solution that worked for me with 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 }
Then you just have to pass down an object with the preloadedState property as the second parameter to your render; you can even define a new store inside the render if you want with the "store" property.
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()
})
})
I couldn't find anywhere else to paste my findings regarding redux toolkit and redux-mock-store.
In order to dispatch async thunks and test results you can specify the type of dispatch when creating the mock store.
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(....);
});
});
As of January 2023 it is no longer recommended to mock the store in redux, see the docs here and this answer for more information.
I am using jest and enzyme library for react testing with create-react-app boilerplate.
With the running of suite and test I am get in above error..
Could not found any solution yet.
Let me know if any solution.
TypeError: Cannot read property 'subroute' of undefined
Yes, I was rendering the connected component with passing the props into it.
So with that purpose all we need to pass store element into the Provider, and mount the component into it.
So all we need to understand is :
Mount: It will render the deep element of props and component associated with it.
Shallow: It will render the the first component of the top layer, not going the deep connected component as I was doing before with shallow.
Here are the code for and complete solution:
import { mountWrap } from '../contextWrap'
import { Provider } from 'react-redux'
import sinon from 'sinon'
import Login from '../components/Login/'
// import makeStore from '../redux/createStore'
import React from 'react'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
const mockStore = configureMockStore([ thunk ])
const authDetails = {
'authDetails' : {
Terms :''
}
}
const match = {
params : {}
}
let actionSpy = sinon.spy()
let actionHistorySpy = sinon.stub({})
let authDetails_ = sinon.stub(authDetails)
let store
let component
/* eslint-disable */
describe('tests for MyContainerComponent', () => {
beforeEach(() => {
store = mockStore(authDetails)
component = mountWrap(<Provider store={ store }>
<Login history={actionHistorySpy} match={match} setGlobalLoaderStatus= {actionSpy} userDetail={authDetails_} />
</Provider>)
})
it('renders container', () => {
console.log(component.debug())
})
})
Whats'up,
I am trying to test some react components that uses redux.
The default behavior should load by a rest call a list of options in a select input. This call is on the method componentDidMount() in my component.
The component works fine, but I cannot simulate the same behavior in my tests.
I cannot call the method componentDidMount() from my instance wrapped by Provider.
Can anyone help me with this
import React from 'react'
import {expect} from 'chai'
import {mount, shallow} from 'enzyme'
import sinon from 'sinon'
import { reducer as formReducer } from 'redux-form'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import ConnectedComponent from '../../../src/components/Component'
describe('Component <Component />', () => {
let store = createStore(combineReducers({ form: formReducer }))
let wrapper = mount(<Provider store={store}><ConnectedComponent /></Provider>)
// this call does not works
wrapper.instance().componentDidMount()
it('should load select input on component mount', () => {
expect(wrapper.find('select option')).to.have.length(12)
})
})
I was able to do like the following :
import React from 'react';
import {connect} from "react-redux";
export class Mock extends React.Component {
constructor(props) {
super(props);
}
myMethod() {
return 123
}
render() {
return (
<div>Test</div>
)
}
}
Mock = connect()(Mock);
export default Mock;
Jest test snippet :
const wrapper = mount(
<Provider store={store}>
<Mock/>
</Provider>
)
let result = wrapper.find(Mock).children().instance().myMethod();
expect(result).toEqual(123);
hope that helps someone!
I'm trying to test a React component with enzyme and mocha as follows
import { mount, shallow } from 'enzyme';
import React from 'react';
import chai, { expect } from 'chai'
import chaiEnzyme from 'chai-enzyme'
import sinon from 'sinon'
import MyComponent from 'myComponent'
chai.use(chaiEnzyme())
describe('MyComponent', () => {
const store = {
id: 1
}
it ('renders', () => {
const wrapper = mount(<MyComponent />, {context: {store: store}})
})
})
haven't actually written the test as it fails at the declaration of wrapper
Error message: TypeError: _this.store.getState is not a function
No idea what the problem is and cant find anything addressing this!
Any help would be great!
This error means that store can't get the state correctly.
I would recommend mocking the store using redux-mock-store
and import configureStore
import configureStore from 'redux-mock-store';
then mock the state by doing this
const initialState = { id: 1 };
const mockStore = configureStore();
and you can continue by wrapping your component with provider
import { Provider } from 'react-redux'; // add this to the top of your file
const wrapper = mount(
<Provider store={mockStore(initialState)}>
<MyComponent />
</Provider>,
);
Also, shouldn't chai.user() be chai.use() in your code example?
I have this container
import { connect } from 'react-redux'
import { createType, getTypes } from '../modules/type'
import Type from '../components/Type'
const mapDispatchToProps = {
createType,
getTypes
}
const mapStateToProps = state => ({
types: state.type.types
})
export default connect(mapStateToProps, mapDispatchToProps)(Type)
and I would like to test it using enzyme. To do it, I'm using this test
import React from 'react'
import { Provider } from 'react-redux'
import { mount } from 'enzyme'
import TypeContainer from 'routes/Type/containers/TypeContainer'
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
const mockStore = configureMockStore([ thunk ]);
const mockStoreInitialized = mockStore({
type: {
types: [
{id: 1, name: 'type 1'}
]
}
});
describe.only('(Route) Type', () => {
it('should get container', () => {
const wrapper = mount(
<Provider store={mockStoreInitialized}>
<TypeContainer />
</Provider>
)
expect(wrapper.find(TypeContainer).prop('types')).to.deep.equal([{id: 1, name: 'type 1'}])
})
})
The test is failing (at the assertion level) because wrapper.find(TypeContainer).props() is empty. I can not find find why.
The strange thing is that if I check the coverage report, my test passed into the mapStateToProps function.
Did I missed something ?
TypeContainer won't have a prop called types, it will pull types from the store and pass it to Type, which will have a prop called types. So it's not that mapStateToProps is not doing the right thing; it's just you're making assertions against the wrong object.