Skip to content

Instantly share code, notes, and snippets.

@pylnata
Last active April 3, 2023 02:59
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save pylnata/43821fb253557254afcbee0288e97640 to your computer and use it in GitHub Desktop.
Save pylnata/43821fb253557254afcbee0288e97640 to your computer and use it in GitHub Desktop.
Jest+Enzyme example unit test with SHALLOW for React component using useEffect and (useDispatch, useSelector) hooks
export const search = (query) => ({type: "SEARCH", query })
// we do it in such way to be able to mock it in test
import { useSelector as originalUseSelector, useDispatch as originalUseDispatch } from "react-redux";
export const useSelector = (state) => originalUseSelector(state);
export const useDispatch = () => originalUseDispatch();
import React from "react";
export const Recipeitem = (props) => {
return (<div>
{props.title}
</div>)
}
import React from "react";
import { Spinner } from "reactstrap";
import { useDispatch, useSelector } from "./react-redux-hooks";
import RecipeItem from "./RecipeItem";
import { search } from "./actions";
const RecipeList = props => {
const dispatch = useDispatch();
const { recipies, isLoading, error } = useSelector((state) => ({
isLoading: state.isLoading,
recipies: state.recipies,
error: state.error
}));
React.useEffect(() => { // important to write React.useEffect and not import useEffect
dispatch(search());
}, [dispatch]);
let result = null;
if (isLoading) {
result = <Spinner />;
} else if (error) {
result = error.message;
} else if (recipies.length > 0) {
result = recipies.map(item => (
<RecipeItem recipe={item} key={item.id} />
));
}
return (
<div>{result}</div>
);
};
export default RecipeList;
import React from "react";
import { configure, shallow } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import configureStore from "redux-mock-store";
import * as ReactReduxHooks from "./react-redux-hooks";
import RecipeList from "./RecipeList";
import RecipeItem from "./RecipeItem";
configure({ adapter: new Adapter() });
describe("RecipeList", () => {
let wrapper;
let useEffect;
let store;
const mockUseEffect = () => {
useEffect.mockImplementationOnce(f => f());
};
beforeEach(() => {
store = configureStore()({
recipies: [{id: 1, title: 'Ice Cream'}, {id: 2, title: 'Soup Cream'}, {id:3, title: 'Cream with fruits'}],
isLoading: false,
error: null
});
useEffect = jest.spyOn(React, "useEffect");
mockUseEffect(); // important to do it twice
mockUseEffect();
jest
.spyOn(ReactReduxHooks, "useSelector")
.mockImplementation(state => store.getState());
jest
.spyOn(ReactReduxHooks, "useDispatch")
.mockImplementation(() => store.dispatch);
wrapper = shallow(<RecipeList store={store} />);
});
describe("on start", () => {
it("dispatch search action to store", () => {
const actions = store.getActions();
expect(actions).toEqual([{ type: "SEARCH", query: "cream" }]);
});
});
it("should render RecipeItem components if gets recipies array from store", () => {
expect(wrapper.find(RecipeItem)).toHaveLength(3);
});
});
@corocoto
Copy link

@pylnata, thanks a lot! It's very helpful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment