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.
Related
I was testing useSelector using the following code below. Although when I looked at my coverage report I saw in red that it wasn't actually being tested... Is there a way to test those lines?
import React from 'react';
import { render } from '../../../test-utlities/test-utlities';
import IndividualInvestor from './IndividualInvestor';
import * as redux from 'react-redux';
describe('Individual Investor', () => {
const useSelectorMock = jest.spyOn(redux, 'useSelector');
const useDispatchMock = jest.spyOn(redux, 'useDispatch');
beforeEach(() => {
useSelectorMock.mockClear();
useDispatchMock.mockClear();
});
test('Makes sure it reaches questionaire', async () => {
useSelectorMock.mockReturnValue({
offering: {
payment_enabled: false,
_id: '61aa480e4e18c1bbfba8f83d',
},
payment_enabled: false,
_id: '61aa480e4e18c1bbfba8f83d',
});
const dummyDispatch = jest.fn();
useDispatchMock.mockReturnValue(dummyDispatch);
Yes, actually run the code :) By mocking useSelector, you've skipped running all of its actual logic that takes your selector function and runs it when needed.
We would strongly recommend against mocking useSelector and useDispatch. Instead, create an actual Redux store in the test, and wrap the component under test with a <Provider>.
You can see examples of how to do this in a reusable render() test util in our docs guide on testing:
https://redux.js.org/usage/writing-tests#components
In addition to what markerikson said i managed to actually test the code with this
wrapper for the component with the provider
// test-utils.js
import React from 'react';
import { render as rtlRender } from '#testing-library/react';
import { configureStore } from '#reduxjs/toolkit';
import { Provider } from 'react-redux';
// Import your own reducer
import rootReducer from '../redux/reducer/combineReducers';
import { BrowserRouter as Router } from 'react-router-dom';
function render(
ui,
{
preloadedState,
store = configureStore({ reducer: rootReducer, preloadedState }),
...renderOptions
} = {}
) {
function Wrapper({ children }) {
return (
<Provider store={store}>
<Router>{children}</Router>
</Provider>
);
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}
// re-export everything
export * from '#testing-library/react';
// override render method
export { render };
Then i used that function render
const store = createStore(
rootReducer,
{
formBody: {
offering: {
data: {
payment_enabled: false,
_id: '61aa480e4e18c1bbfba8f83d',
},
},
},
},
applyMiddleware(thunk)
);
const { container } = render( <SomeComponent/>,
//Pass in store here!
{
store: store,
}
);
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 react-redux-datatable plugin for table creation.
react-redux-datatable usage: https://sean-ww.github.io/react-redux-datatable/get-started/get-started.html
I have followed the steps given in above docs.
But i am not able to resolve this error. I have included store in Provider Container, but still i am getting the issue.
I am getting error,
Could not find "store" in either the context or props of "Connect(DataTableContainer)". Either wrap the root component in a Provider, or explicitly pass "store" as a prop to "Connect(DataTableContainer)".
I have included the two files index.js and App.js
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { applyMiddleware, createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import logger from 'redux-logger'
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import supplierReducer from './store/supplierreducer';
import { DataTableReducer } from 'react-redux-datatable';
import { connect } from 'net';
const rootReducer = combineReducers({
supplierReducer,
DataTableReducer
});
const store = createStore(
rootReducer,
applyMiddleware(logger)
);
const app = (
<Provider store={store}>
<App />
</Provider>
);
ReactDOM.render(app, document.getElementById('root'));
serviceWorker.unregister();
App.js
import DataTable from 'react-redux-datatable';
class App extends Component {
render() {
const apiLocation = 'https://api.myjson.com/bins/j59v8';
const tableSettings = {
tableID: 'BasicDataTable',
keyField: 'request_id',
tableColumns: [
{
title: 'Ref',
key: 'request_id',
},
{
title: 'First Name',
key: 'first_name',
}
],
};
return (
<div>
<DataTable
tableSettings={this.tableSettings}
apiLocation={this.apiLocation}
/>
</div>
);
}
}
const mapStateToProps = state => {
return {
name: state.name,
address: state.address,
supplierData: state.supplierData
};
};
const mapDispatchToProps = dispatch => {
return {
onSupplierDataListSuccess: (response) => dispatch({
type: 'SUPPLIER_DATA_SUCCESS',
response: response
}),
onSupplierDataListFailure: () => dispatch({
type: 'SUPPLIER_DATA_FAILURE'
})
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
I am getting error, Could not find "store" in either the context or props of "Connect(DataTableContainer)". Either wrap the root component in a Provider, or explicitly pass "store" as a prop to "Connect(DataTableContainer)".
in order to access the store you will need to use a mapStateToProps function. Check out the docs here: Connect: Extracting Data with mapStateToProps
I have error with second test. What is wrong with them? I doing all by docs, but have error: "Cannot read property 'prop' of undefined". I tried with using Provider, but it doesn't help me, maybe i do something wrong?
Can i somehow debug it?
BoardContainer.test.js
import React from 'react'
import configureStore from 'redux-mock-store'
import ConnectedBoard,{Board} from './BoardContainer'
import { shallow, configure,mount} from 'enzyme'
import Adapter from 'enzyme-adapter-react-16';
import {Provider} from 'react-redux'
import PropTypes from 'prop-types';
configure({adapter: new Adapter()});
const boards = [{
id: 1,
title: 'first_board'
}, {
id: 2,
title: 'second_board',
}];
describe('>>>H O M E --- REACT-REDUX (Shallow + passing the {store} directly)',()=> {
const initialState = {boards: boards};
const mockStore = configureStore();
let store, wrapper;
beforeEach(()=>{
store = mockStore(initialState);
wrapper = shallow( <Provider store={store}><ConnectedBoard /></Provider> );
})
it('+++ render the connected(SMART) component', () => {
expect(wrapper.length).toEqual(1);
});
it('+++ check Prop matches with initialState', () => {
expect(wrapper.prop('boards')).toEqual(initialState.boards);
});
})
BoardContainer.js
import React, {Component} from 'react'
import {connect} from 'react-redux'
import {loadBoards} from '../../actions/boardJS'
import Board from '../../components/Board/Board'
export class BoardContainer extends Component {
getBoards() {
fetch(process.env.REACT_APP_DEV_API_URL + `todo_lists`)
.then(res => res.json())
.then(data => {
this.props.dispatch(loadBoards(data))
})
}
componentDidMount() {
this.getBoards()
}
render() {
return <Board/>
}
}
const mapStateToProps = state => {
return {
boards: state.boards
}
}
export default connect(mapStateToProps)(BoardContainer)
If someone know solution it would be nice :)
It looks like you are looking at the props of the Provider Component not the ConnectedBoard component.
I'd also be mindful of the below:
"When called on a shallow wrapper, .prop(key) will return values for props on the root node that the component renders, not the component itself." - https://airbnb.io/enzyme/docs/api/ShallowWrapper/prop.html
Can you elaborate a bit, you say that property 'prop' is missing, but that would mean that wrapper does not have a ShallowWrapper assigned. Which would mean beforeEach has not executed... I doubt that's the case if test 1 was successful.
Was the error infact that props itself is not defined? When you try to access the property 'boards'
I currently have a component like so:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getDataAction } from ' './my-component';
export class MyComponent extends { Component } {
componentWillMount() {
this.props.getData();
}
render(){
<div>
this.props.title
</div>
}
}
const mapStateToProps = (state) => ({
title: state.title
});
const mapDispatchToProps = (dispatch) ({
getData() {
dispatch(getDataAction());
}
});
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
and I am trying to shallow render test it using jest and enzyme.
test:
import React from 'react';
import { shallow } from 'enzyme';
import { MyComponent } from './index';
it('renders without crashing', () => {
shallow(<MyComponent getData={jest.fn()} />);
});
My question is, is this the conventional way to mock? Jest official docs don't mention specifically about mocking props and this post Using Jest to mock a React component with props is about testing with full mounting instead.
Is there another way to mock dispatchToProps? In this example there is only one, but what if I have a lot of functions in dispatchToProps?
Side Question: in my real file, I have a reference to a value like this.props.information.value which I expect to throw an error like cannot get value of undefined since information is not mocked/defined, but it doesn't. It's only when functions are not present that an error is thrown.
You can export mapDispatchToProps and write tests for it by importing it in your tests.
Add export { mapDispatchToProps }; at the end of your MyComponent.js
Create MyComponent.tests.js file beside MyComponent.js
import configureMockStore from 'redux-mock-store';
import thunkMiddleware from 'redux-thunk';
import { mapDispatchToProps } from './MyComponent';
const configMockStore = configureMockStore([thunkMiddleware]);
const storeMockData = {};
const mockStore = configMockStore(storeMockData);
describe('mapDispatchToProps', () => {
it('should map getDataAction action to getData prop', () => {
// arrange
const expectedActions = [getDataAction.type];
const dispatchMappedProps = mapDispatchToProps(mockStore.dispatch);
// act
dispatchMappedProps.getData();
// assert
expect(mockStore.getActions().map(action => action.type)).toEqual(expectedActions);
}
});
Here I have used thunk, just to let you know that how to do it if there are middlewares configured in your store setup.
Here getDataAction can also be a function instead of a simple action like { type: 'FETCH_DATA' } if you are using middlewares like thunks. However, the approach to test is same except that you will create expectedActions with explicit action types like const expectedActions = ['FETCH_CONTACTS']
Here FETCH_CONTACT is another action dispatched in your thunk i.e getDataAction