From 09f4571fde7b958f82eef3f87a98bb7fe22259ac Mon Sep 17 00:00:00 2001 From: Marcello Lamonaca Date: Fri, 19 Mar 2021 15:18:20 +0100 Subject: [PATCH] Redux Tests Notes --- JavaScript/React/Redux Tests.md | 151 ++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 JavaScript/React/Redux Tests.md diff --git a/JavaScript/React/Redux Tests.md b/JavaScript/React/Redux Tests.md new file mode 100644 index 0000000..cb3f7eb --- /dev/null +++ b/JavaScript/React/Redux Tests.md @@ -0,0 +1,151 @@ +# Redux Testing + +## Tests for Connected Components + +Connected components are warpped in a call to `connect`. Way of solving the problem: + +- Wrap component with ``. Added benefit: new store dedicated to tests. +- Add named export for unconncted component. + +In `Component.js`: + +```js +export function Component(props) { /* ... */ } // export unconnected component + +export default connect(mapStateToProps, mapDispatchToProps)(Component) // default export of connected component +``` + +In `Component.test.js`: + +```js +import React from "react"; +// import enzyme or react testing library + +// import mock data +import { Component } from "path/to/Component"; // import unconnected component + +// factory to setup test easily +function testHelper(args) { + const defaultProps = { + /* default value for props in each test and required props */, + history = {} // normally injected by react-router, could also import the router + }; + + const props = { ...defaultProps, ...args }; + return mount(); // or render if using react testing library +} + +it("test description", () => { + const dom = testHelper(); + + // simulate page interation + dom.find("selctor").simulate(""); + + // find changed component + // test expected behaviour of component +}); +``` + +## Tests for Action Creators + +```js +import * as actions from "path/to/actionCreators"; +// import eventual action types constants +// import mock data + +describe("Async Actions", () => { + afterEach(() => { + fetchMock.restore(); // init fecth mok for each test + }); + + it("test description", () => { + // mimic API call + fetchMock.mock( + "*", // capture any fetch call + { + body: /* body contents */, + headers: { "content-type": "application/json" } + } + ); + + const expectedActions = [ + { type: TYPE, /* ... */ }, + { type: TYPE, /* ... */ } + ]; + + const store = mockStore({ data: value, ... }); + return store.dispatch(actions.actionCreator()) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); + }); +}); +``` + +## Tests for Reducers + +```js +import reducer from "path/to/reducer"; +import * as actions from "path/to/actionCreators"; + +it("test description", () => { + const initialState = /* satte before the action */; + const stateUpdate = /* modified state */; + + const action = actions.actionCreator(stateUpdate); + + const newState = reducer(initialState, action); + + expect(newState.property).toEqual(actual); +}); +``` + +## Tests for the Store + +```js +import { createStore } from "redux"; + +import rootReducer from "path/to/rootReducer"; +import initialState from "path/to/initialState"; +import * as actions from "path/to/actionCreators"; + +it("test description", () => { + const store = createStore(toorReducer, initialState); + + const stateUpdate = /* modified state */; + const action = actions.actionCreator(stateUpdate); + store.dispatch(action); + + const state = store.getState(); + expect(state).toEqual(stateUpdate); +}); +``` + +## Tests for Thunks + +Thunk testing requires the mocking of: + +- store (using `redux-mock-store`) +- HTTP calls (using `fetch-mock`) + +```js +import thunk from "redux-thunk"; +import fetchMock from "getch-mock"; +import configureMockStore from "redux-mock-store"; + +import * as actions from "path/to/actionCreators"; +// import eventual action types constants +// import mock data + +const middleware = [thunk]; // mock middlewares +const mockStore = configureMockStore(middleware); // mock the store + +it("test description", () => { + const data = /* mock data */ + const expectedAction = { type: TYPE, /* ... */ }; + + const actualAction = actions.actionCreator(data); + + expect(actualAction).toEqual(expectedAction); +}); +```