Testing
Rematch testing works out of the box with many libraries like Jest, Mocha, Ava, and of course works fine with end to end tests like Cypress or Testing Library.
tip
You can check our full suite of test of @rematch/core to check examples of how to test. We're using jest but they should work with any testing provider.
Jest​
This tests are based in Jest, but should be pretty similar in other testing frameworks.
Reducers​
Testing with store.
tsimport {init } from "@rematch/core";import {models ,RootModel } from "./models";Âdescribe ("[count] model", () => {it ("incrementAsync effect should increment given a payload", async () => {conststore =init <RootModel >({models ,});Âawaitstore .dispatch .count .incrementAsync (3);ÂconstmyModelData =store .getState ().count ;expect (myModelData ).toEqual (3);});});
tsimport {init } from "@rematch/core";import {models ,RootModel } from "./models";Âdescribe ("[count] model", () => {it ("incrementAsync effect should increment given a payload", async () => {conststore =init <RootModel >({models ,});Âawaitstore .dispatch .count .incrementAsync (3);ÂconstmyModelData =store .getState ().count ;expect (myModelData ).toEqual (3);});});
Effects​
Testing with store.
tsimport {init } from "@rematch/core";import {models ,RootModel } from "./models";Âdescribe ("[count] model", () => {it ("effect: my incrementAsync effect should do something", async () => {conststore =init <RootModel >({models ,});Âawaitstore .dispatch .count .incrementAsync (3);ÂconstcountData =store .getState ().count ;expect (countData ).toEqual (3);});});
tsimport {init } from "@rematch/core";import {models ,RootModel } from "./models";Âdescribe ("[count] model", () => {it ("effect: my incrementAsync effect should do something", async () => {conststore =init <RootModel >({models ,});Âawaitstore .dispatch .count .incrementAsync (3);ÂconstcountData =store .getState ().count ;expect (countData ).toEqual (3);});});
Testing effects directly.
tsimport {count } from "./count";Âdescribe ("myModel model", () => {it ("effect: my effectName should do something", async () => {constreducerMockFn =jest .fn ();Â// bind the functions you want to checkawait (count .effects as any).incrementAsync .call ({reducerThatIsGoingToBeCalled :reducerMockFn },{payload : "" });Â// checking if it was calledexpect (reducerMockFn ).toHaveBeenCalled ();Â// checking if it was called with the expected paramsexpect (reducerMockFn ).toHaveBeenCalledWith ("something");});});
tsimport {count } from "./count";Âdescribe ("myModel model", () => {it ("effect: my effectName should do something", async () => {constreducerMockFn =jest .fn ();Â// bind the functions you want to checkawait (count .effects as any).incrementAsync .call ({reducerThatIsGoingToBeCalled :reducerMockFn },{payload : "" });Â// checking if it was calledexpect (reducerMockFn ).toHaveBeenCalled ();Â// checking if it was called with the expected paramsexpect (reducerMockFn ).toHaveBeenCalledWith ("something");});});
Testing Library​
To get started with Testing Library tests since they're similar to e2e you have to wrap your React application with the React-Redux Provider:
testUtils.tsxtsximportReact from "react"import {render } from "@testing-library/react"import {Provider } from "react-redux"import type {Store } from "redux"Âexport constrenderWithRematchStore = (ui :React .ReactElement ,store :Store ) =>render (ui , {wrapper : ({children }) => <Provider store ={store }>{children }</Provider >,})
testUtils.tsxtsximportReact from "react"import {render } from "@testing-library/react"import {Provider } from "react-redux"import type {Store } from "redux"Âexport constrenderWithRematchStore = (ui :React .ReactElement ,store :Store ) =>render (ui , {wrapper : ({children }) => <Provider store ={store }>{children }</Provider >,})
Now, we can just use this renderWithRemathStore instead of the native render function that @testing-library exposes, to render any component that is connected to Rematch store.
(For ex: using useSelector, or useDispatch)
Imagine an scenario where we have a component called <ButtonCounter> that recovers information of the count model and increments the value:
tsximportReact , {useEffect } from "react"import {useDispatch ,useSelector } from "react-redux"import type {RootState ,Dispatch } from "./store"Âexport constButtonCounter = () => {constdispatch =useDispatch <Dispatch >()constcounter =useSelector ((rootState :RootState ) =>rootState .count )Âreturn (<div ><span aria-label ="Counter">Current counter: {counter }</span ><button aria-label ="Increment Button"type ="button"onClick ={() =>dispatch .count .incrementAsync (1)}>Increment Asynchronous</button ></div >)}
tsximportReact , {useEffect } from "react"import {useDispatch ,useSelector } from "react-redux"import type {RootState ,Dispatch } from "./store"Âexport constButtonCounter = () => {constdispatch =useDispatch <Dispatch >()constcounter =useSelector ((rootState :RootState ) =>rootState .count )Âreturn (<div ><span aria-label ="Counter">Current counter: {counter }</span ><button aria-label ="Increment Button"type ="button"onClick ={() =>dispatch .count .incrementAsync (1)}>Increment Asynchronous</button ></div >)}
We could test with Testing Library that pressing in that buttons correctly dispatches the incrementAsync effect and the counter value it's correctly refreshed.
tsximportReact from "react";import {screen } from "@testing-library/react";import {store } from "./store";import {renderWithRematchStore } from "./testUtils";Âimport {ButtonCounter } from "./ButtonCounter";Âdescribe ("ButtonCounter", () => {it ("should be rendered correctly", async () => {renderWithRematchStore (<ButtonCounter />,store );expect (screen .getByLabelText ("Increment Button")).toBeInTheDocument ();awaituserEvent .click (screen .getByLabelText ("Increment Button"));expect (screen .getByLabelText ("Increment Button")).toBeInTheDocument ();expect (screen .getByLabelText ("Counter")).toEqual ("Current counter: 1");});});
tsximportReact from "react";import {screen } from "@testing-library/react";import {store } from "./store";import {renderWithRematchStore } from "./testUtils";Âimport {ButtonCounter } from "./ButtonCounter";Âdescribe ("ButtonCounter", () => {it ("should be rendered correctly", async () => {renderWithRematchStore (<ButtonCounter />,store );expect (screen .getByLabelText ("Increment Button")).toBeInTheDocument ();awaituserEvent .click (screen .getByLabelText ("Increment Button"));expect (screen .getByLabelText ("Increment Button")).toBeInTheDocument ();expect (screen .getByLabelText ("Counter")).toEqual ("Current counter: 1");});});
Thanks to aria-label tags inside our ButtonCounter component we can query this DOM elements with the screen property exported by Testing Library.
We recover these DOM elements and we use the Testing Library assertions like .toBeInTheDocument() to make sure that everything runs fine.