Mock redux action creators with jest
Why
You're cool. You're going to test your React smart components (aka containers in redux) and make assumptions what they call necessary redux action creators. Well, you can have few ways to achieve this:
Path 1: action creators binding
{ // some render... not important } { thisprops; } { return sendMyAction: };
then in test
const spy = jest;;
It works well in plain Javascript, but quickly becomes headache in Typescript:
You must to declare your actions here to not lose the function types, which is the boring task. Also, you may have some middleware which converts your action to promises/observables (very common case) and you'll probably be ended in declaring separate function types with promisified results.
dispatch()
and call action creators directly
Path 2: By using injected import sendMyAction from "./actions"; { thisprops; }
This is also neat in Typescript (since you don't need to declare your actions in component props again) and you can do some tricks with generics and redux's dispatch()
to automatically obtain correct result type from request action type. Nice!
Unfortunately this way has very big disadvantages when it comes to testing:
- You're depending on actual action creator function in your component test, which is not very good (Action creator may have some logic additionally):
- Hard to test
- Not possible to test thunks action creators
- In case if it will be handled by some middleware which will return different result (for ex. promise), you must mock your action creator
For example:
{ return type: "MY_ACTION" payload: a: a === true ? "truthy": "falsy" b } { thisprops; }
test:
const dispatch = jest;const w = ;w;
- You must assert your dispatch in test with something like:
;// or;
Variant 1 looks OK, but is not always achievable. For example if you have some param validation in your creator and throw error - it won't be possible to test it without mocking AC.
- Thunk action creators won't work
{ return { ; }}
;
Will always fail
- Mocking action:
async { const res = await thisprops; // do something with res ressomeval; } //test ;jest; myAction;
Again, this will quickly become very boring if you have many actions creators and even much boring when using Typescript
This library comes to resque:
Component { // some render } { thisprops; } async { const res = await thisprops; await thisprops; }
test:
import mockActionCreators createDispatchMockImplementation from "jest-mock-action-creators";import myAction from "../myAction";import anotherAction from "../anotherAction"; // Automatically mock your action creator modules. When using babel transformer it will insert jest.mock() for their module paths;// or replaceActionCreators(myAction, anotherAction); // Doesn't insert jest.mock() for their module paths, expects myAction and anotherAction be already mocked (i.e. jest.fn()) const dispatch = jest;const wrapper = ;wrapper; // Pretty easy, isn't it?;not; // Return { val: "test2" } when calling myAction();; wrapper;;;
Installation
npm install jest-mock-action-creators --save-dev
Add jest-mock-action-creators/babel
to your plugins in .babelrc or .babelrc.js
When using typescript and ts-jest
, enable babel processing in ts-jest
(enabled by default) and tell it to use .babelrc:
Note: Specifying plugins: []
in ts-jest babel configuration won't work
and finally import in your test:
;
Want to mock only the specified action creators?
It's possible with using jest-easy-mock, use this configuration:
"jest-easy-mock" requireActual: true identifiers: name: "jest.mockObj" remove: true type: "name" name: "jest.mockFn" remove: true type: "mock" name: "replaceActionCreators" remove: false type: "mock" "jest-mock-action-creators/babel" mockIgnoreExpressions: "mock" "doMock" "mockObj" "mockFn"
and in the test:
;; ; ;