How do you debug a shallow rendered enzyme test? - reactjs

I am trying to fix a failing test in my react-redux app. When I dive and dive again into my component, I expect to see the JSX within it. However, I don't see anything.
Here is my component -
const Dashboard = (props) => {
if (props.isSignedIn)
return (
<div className="dashboard">
<h1>Welcome</h1>
</div>
);
return null;
};
const mapStateToProps = (state) => {
return { isSignedIn: state.auth.isSignedIn };
};
export default connect(mapStateToProps, { signIn, signOut })(Dashboard);
Here is my failing test :-
const setup = (initialState = {}) => {
const store = storeFactory(initialState);
const wrapper = shallow(<Dashboard store={store} />).dive().dive();
return wrapper;
};
describe("on render", () => {
describe("the user is signed in", () => {
let wrapper;
beforeEach(() => {
const initialState = { isSignedIn: true };
wrapper = setup(initialState);
});
it("renders the dashboard", () => {
const component = wrapper.find("dashboard");
expect(component.length).toBe(1);
});
});
My store factory :-
export const storeFactory = (initialState) => {
const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
console.log(initialState);
return createStoreWithMiddleware(rootReducer, initialState);
};
My test error :-
● the user is signed in › renders the dashboard
expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 0
When I dive one time it looks like this :-
<Dashboard store={{...}} isSignedIn={{...}} signIn={[Function]} signOut={[Function]} />}
but when I try to see the JSX inside of the dashboard component I see nothing?

I'm pretty sure your setup isn't working because you're trying to shallow mount a redux connected component -- which is a higher-order component (HOC) wrapping another component that can't be properly dived into due to enzyme's shallow limitations.
Instead, you have two options:
Option 1.) Recommended: Export the Dashboard component and make assertions against it using shallow or mount (I mostly use mount over shallow specifically to avoid excessive and repetitive .dive() calls).
First export the unconnected component:
export const Dashboard = (props) => {
if (props.isSignedIn)
return (
<div className="dashboard">
<h1>Welcome</h1>
</div>
);
return null;
}
export default connect(...)(Dashboard)
Then in your test, import the Dashboard component (not the default connected export):
import { mount } from "enzyme";
import { Dashboard } from "./Dashboard"; // importing named export "Dashboard"
const initialProps = {
isSignedIn: false
}
const wrapper = mount(<Dashboard { ...initialProps } />); // alternatively, you can use shallow here
// now manipulate the wrapper using wrapper.setProps(...);
or
Option 2.) Not recommended: Wrap the component in a real Provider with a real store and mount the connected HOC:
import { mount } from "enzyme";
import { Provider } from "redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../path/to/reducers";
import types from "../path/to/types";
import Dashboard from "./Dashboard"; // importing connected default export
const store = createStore(rootReducer, undefined, applyMiddleware(thunk));
const initialProps = {
isSignedIn: false
};
const wrapper = mount(
<Provider store={store}>
<Dashboard { ...initialProps } />
</Provider>
);
// now you'll dispatch real actions by type and expect the redux store to change the Dashboard component
For more testing information, please take a look at this answer, which covers a similar example (skip to the bullet points; ignore the MemoryRouter pieces; and, while the example is a bit old, the testing pattern is the same).
The reason I'd recommend option 1 over option 2 is that it's much easier to manage, as you're directly manipulating the component you're testing (not a HOC wrapping your component). Option 2 is only useful if you absolutely need to test the entire workflow of redux to the connected component. I find option 2 to be mostly overkill as you can unit test each piece (actions, reducers, and unconnected components) individually and achieve the same testing coverage. In addition, as I mentioned in this example, I find redux-mock-store to be mostly useful for unit testing redux actions.
On a side note, you can see what enzyme sees by using console.log(wrapper.debug()); in a test!
Example unconnected test:
import React, { createElement } from "react";
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import { mount } from "enzyme";
import { MemoryRouter } from "react-router-dom";
import { Dashboard } from "./App.js";
configure({ adapter: new Adapter() });
const initialProps = {
isSignedIn: false
};
describe("Unconnected Dashboard Component", () => {
let wrapper;
beforeEach(() => {
/*
This code below may be a bit confusing, but it allows us to use
"wrapper.setProps()" on the root by creating a function that first
creates a new React element with the "initialProps" and then
accepts additional incoming props as "props".
*/
wrapper = mount(
createElement(
props => (
<MemoryRouter initialEntries={["/"]}> // use memory router for testing (as recommended by react-router-dom docs: https://reacttraining.com/react-router/web/guides/testing)
<Dashboard {...props} />
</MemoryRouter>
),
initialProps
)
);
});
it("initially displays nothing when a user is not signed in", () => {
expect(wrapper.find(".dashboard").exists()).toBeFalsy();
});
it("displays the dashboard when a user is signed in", () => {
wrapper.setProps({ isSignedIn: true });
expect(wrapper.find(".dashboard").exists()).toBeTruthy();
});
});
Working example (click on the Tests tab to run tests):
Reuseable HOC wrapper:
utils/HOCWrapper/index.js
import React, { createElement } from "react";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import { MemoryRouter } from "react-router-dom";
import { mount } from "enzyme";
import thunk from "redux-thunk";
import rootReducer from './path/to/reducers';
const middlewares = applyMiddleware([thunk]);
export const store = createStore(rootReducer, null, middlewares);
/**
* Factory function to create a mounted MemoryRouter + Redux Wrapper for a component
* #function HOCWrapper
* #param {node} Component - Component to be mounted
* #param {object} initialProps - Component props specific to this setup.
* #param {object} state - Component initial state for setup.
* #param {array} initialEntries - Initial route entries for MemoryRouter.
* #param {object} options - Optional options for enzyme's "mount"
* #function createElement - Creates a wrapper around passed in component (now we can use wrapper.setProps on root)
* #returns {MountedRouterWrapper}
*/
export const HOCWrapper = (
Component,
initialProps = {},
state = null,
initialEntries = ["/"],
options = {},
) => {
const wrapper = mount(
createElement(
props => (
<Provider store={store}>
<MemoryRouter initialEntries={initialEntries}>
<Component {...props} />
</MemoryRouter>
</Provider>
),
initialProps,
),
options,
);
if (state) wrapper.find(Component).setState(state);
return wrapper;
};
export default HOCWrapper;
To use it, import the HOCWrapper function:
import Component from "./Example";
import HOCWrapper from "./path/to/utils/HOCWrapper";
// if you need access to store, then...
// import HOCWrapper, { store } from "./path/to/utils/HOCWrapper";
const initialProps = { ... };
const initialState = { ... }; // optional (default null)
const initialEntries = [ ... ]; // optional (default "/")
const options = { ... }; // optional (default empty object)
// use the JSDoc provided above for argument breakdowns
const wrapper = HOCWrapper(Component, initialProps, initialState, initialEntries, options); // not all args are required, just the "Component"

Related

How to mock store in redux toolkit

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.

how react-redux gets the store object of provider?

Here is a basic react-redux app (sandbox):
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { useDispatch, Provider, useSelector, useStore } from "react-redux";
const App = () => {
const store = useStore(); // <- how it gets the store object of Provider ?
const state = useSelector(s => s);
return <div>{state}</div>;
};
ReactDOM.render(
<Provider store={createStore((state, action) => 5)}>
<App />
</Provider>,
document.getElementById("root")
);
Now my question is:
How hooks like useStore gets the store object we set in <Provider store={store}> ?
If it was dom, we could use this.closest('.provider').getAttribute('store') to get the store attribute of the provider element in parents. But how we can do it in react?
I'm asking it because i want to understand how react-redux works behind the scenes.
Thanks.
react-redux uses a provider that holds all of the properties it works with. It allows you to pull the internals from the provider through nice APIs such as the hooks API (useStore, useDispatch), or through the connect() HOC API.
To help you visualise it in a simpler way, let's write a "mini" react-redux using React Context API
import React, { createContext, useContext } from 'react';
const InternalProvider = createContext();
/**
* This is the `Provider` you import from `react-redux`.
* It holds all of the things child components will need
*/
const Provider = ({ store, children }) => {
/**
* This `context` object is what's going to be passed down
* through React Context API. You can use `<Consumer>` or
* `useContext` to get this object from any react-redux-internal
* child component. We'll consume it on our `useStore` and
* `useDispatch` hooks
*/
const context = {
getStore: () => store,
getDispatch: (action) => store.dispatch,
};
return (
<InternalProvider value={context}>
{children}
</InternalProvider>
);
}
/**
* These are the hooks you import from `react-redux`.
* It's dead simple, you use `useContext` to pull the `context`
* object, and voila! you have a reference.
*/
const useStore = () => {
const context = useContext(InternalProvider)
const store = context.getStore();
return context;
};
const useDispatch = () => {
const { getDispatch } useContext(InternalProvider);
return getDispatch();
};
/***************************************
* Your redux-aware components
*
* This is how you consume `react-redux` in your app
*/
const MyComponent = () => {
const store = useStore();
const dispatch = useDispatch();
return <>Foo</>
}
const App = () => (
<Provider store={store}>
<MyComponent />
</Provider>
)

Testing custom hook: Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

I got a custom hook which I want to test. It receives a redux store dispatch function and returns a function. In order to get the result I'm trying to do:
const { result } = renderHook(() => { useSaveAuthenticationDataToStorages(useDispatch())});
However, I get an error:
Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a
It happens because of the useDispatch and that there is no store connected. However, I don't have any component here to wrap with a provider.. I just need to test the hook which is simply saving data to a store.
How can I fix it?
The react hooks testing library docs go more into depth on this. However, what we essentially are missing is the provider which we can obtain by creating a wrapper. First we declare a component which will be our provider:
import { Provider } from 'react-redux'
const ReduxProvider = ({ children, reduxStore }) => (
<Provider store={reduxStore}>{children}</Provider>
)
then in our test we call
test("...", () => {
const store = configureStore();
const wrapper = ({ children }) => (
<ReduxProvider reduxStore={store}>{children}</ReduxProvider>
);
const { result } = renderHook(() => {
useSaveAuthenticationDataToStorages(useDispatch());
}, { wrapper });
// ... Rest of the logic
});
This is probably a late answer but you can also use this in your test
jest.mock('react-redux', () => {
const ActualReactRedux = jest.requireActual('react-redux');
return {
...ActualReactRedux,
useSelector: jest.fn().mockImplementation(() => {
return mockState;
}),
};
});
This issues is related your test file. You have to declarer provider and store in your test file.
Update or replace your app.test.tsx by below code
NB: Don't forget to install redux-mock-store if you don't have already.
import React from 'react';
import { render } from '#testing-library/react';
import App from './App';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
describe('With React Testing Library', () => {
const initialState = { output: 10 };
const mockStore = configureStore();
let store;
it('Shows "Hello world!"', () => {
store = mockStore(initialState);
const { getByText } = render(
<Provider store={store}>
<App />
</Provider>
);
expect(getByText('Hello World!')).not.toBeNull();
});
});
I got this solution after searching 1 hours.
Thanks a lot to OSTE
Original Solution: Github issues/8145 and solution link
With this solution if you get error like TypeError: window.matchMedia is not a function then solve by this way. add those line to your setupTests.ts file. Original solution link stackoverflow.com/a/64872224/5404861
global.matchMedia = global.matchMedia || function () {
return {
addListener: jest.fn(),
removeListener: jest.fn(),
};
};
I think you can create test-utils.[j|t]s(?x), or whatever you set the name of the file to, like this:
https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/test-utils.tsx
//root(or wherever your the file)/test-utils.tsx
import React from 'react';
import { render, RenderOptions } from '#testing-library/react';
import { Provider } from 'react-redux';
// Import your store
import { store } from '#/store';
const Wrapper: React.FC = ({ children }) => <Provider store={store}>{children}</Provider>;
const customRender = (ui: React.ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => render(ui, { wrapper: Wrapper, ...options });
// re-export everything
export * from '#testing-library/react';
// override render method
export { customRender as render };
Use it like this:
https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/pages/index.test.tsx
//__tests__/pages/index.test.tsx
import React from 'react';
import { render, screen } from '../test-utils';
import Home from '#/pages/index';
describe('Home Pages', () => {
test('Should be render', () => {
render(<Home />);
const getAText = screen.getByTestId('welcome');
expect(getAText).toBeInTheDocument();
});
});
Works for me.
screenshot work
BTW, if you place the test-utils.[j|t]s(?x) or whatever you set the name file place on the directory __test__, don't forget to ignore it on jest.config.js.
//jest.config.js
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/', '<rootDir>/__tests__/test-utils.tsx'],
repo: https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart

How to write unit test redux connected components in next.js app with Jest end Enzyme

In React Single Page App, we need to separate the logic of createStore to another component (usually called <Root />) to reuse it in your test file to let connect function link with the store
Root.js
import React from "react";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "reducers";
import { applyMiddleware } from "redux";
import reduxPromise from "redux-promise";
const appliedMiddlewares = applyMiddleware(reduxPromise);
export default ({ children, initialState = {} }) => {
const store = createStore(reducers, initialState, appliedMiddlewares);
return <Provider store={store}>{children}</Provider>;
};
And then in your test file, to mount or shallow your component, your code should look like this:
import Root from "Root";
let mounted;
beforeEach(() => {
mounted = mount(
<Root>
<CommentBox />
</Root>
);
});
But for the case of Next.JS, the logic to let redux works with it was implemented in _app.js file, with some wrapper components (<Container>, <Component> ) that I do not know how it works so I could not find the way to separate the createStore logic
_app.js
import App, { Container } from "next/app";
import React from "react";
import Root from '../Root';
import withReduxStore from "../lib/with-redux-store";
import { Provider } from "react-redux";
class MyApp extends App {
render() {
const { Component, pageProps, reduxStore } = this.props;
return (
<Container>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</Container>
);
}
}
export default withReduxStore(MyApp);
Anyone knows it ? Many many thanks for helping me solve this.
Possibly I'm late adding a response, but this is what I did and worked!
First I imported the custom app:
import App from "../_app";
import configureStore from "redux-mock-store";
import thunk from "redux-thunk";
import { state } from "../../__mocks__/data";
const middlewares = [thunk];
const mockStore = configureStore(middlewares)(state);
Then I mocked the getInitialProps from the _app.js like:
const context = {
store: mockStore,
req: {
headers: {
cookie: ""
}
}
};
const props = await App.getInitialProps({
ctx: context,
Component: {
getInitialProps: jest.fn(() => {
return Promise.resolve({ ... });
})
}
});
Then, debugging over node_modules\next-redux-wrapper\src\index.tsx I noticed how the initialState must be set.
Then I added the following code:
delete window.__NEXT_REDUX_STORE__;
const wrapper = shallow(<App {...props} initialState={state}/>);
expect(toJson(wrapper)).toMatchSnapshot();
And run the tests, everything now works as expected.
If there is a cleaner solution please let me know.
I hope It works for you!

Why enzyme tests did not work in React.js?

I am using Enzyme tests within Create-React-App. In shallow rendering it works fine, but mount rendering throws error:
TypeError: Cannot read property 'favorites' of undefined
Test file looks like this:
import React, { Component } from "react";
import configureMockStore from "redux-mock-store";
import { shallow, mount } from "enzyme";
import { Provider } from "react-redux";
import Favorites from "../Landing/Favorites";
const mockStore = configureMockStore();
const store = mockStore({});
function setup() {
const props = {
favorites: 42
};
const wrapper = mount(
<Provider store={store}>
<Favorites {...props} />
</Provider>
);
return {
props,
wrapper
};
}
describe("Favorites component", () => {
const { wrapper } = setup();
it("should render list of favorites cards", () => {
expect(wrapper.prop("favorites")).toEqual(42);
});
});
Why did it happen?
.prop works different in mount and shallow. You can check the docs.
http://airbnb.io/enzyme/docs/api/ReactWrapper/prop.html
http://airbnb.io/enzyme/docs/api/ShallowWrapper/prop.html
When using mount, you can directly render Favorites component.
mount(<Favorites {...props} />)

Resources