1 | # jest-mock-extended
|
2 | > Type safe mocking extensions for Jest 🃏
|
3 |
|
4 | [![Build Status](https://travis-ci.com/marchaos/jest-mock-extended.svg?branch=master)](https://travis-ci.com/marchaos/jest-mock-extended)
|
5 | [![Coverage Status](https://coveralls.io/repos/github/marchaos/jest-mock-extended/badge.svg?branch=master)](https://coveralls.io/github/marchaos/jest-mock-extended?branch=master)
|
6 | [![npm version](https://badge.fury.io/js/jest-mock-extended.svg)](https://badge.fury.io/js/jest-mock-extended)
|
7 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
8 | [![npm downloads](https://badgen.net/npm/dw/jest-mock-extended)](https://badge.fury.io/js/jest-mock-extended)
|
9 |
|
10 | ## Features
|
11 | - Provides complete Typescript type safety for interfaces, argument types and return types
|
12 | - Ability to mock any interface or object
|
13 | - calledWith() extension to provide argument specific expectations, which works for objects and functions.
|
14 | - Extensive Matcher API compatible with Jasmine matchers
|
15 | - Supports mocking deep objects / class instances.
|
16 | - Familiar Jest like API
|
17 |
|
18 | ## Installation
|
19 | ```bash
|
20 | npm install jest-mock-extended --save-dev
|
21 | ```
|
22 | or
|
23 | ```bash
|
24 | yarn add jest-mock-extended --dev
|
25 | ```
|
26 |
|
27 | ## Example
|
28 |
|
29 | ```ts
|
30 | import { mock } from 'jest-mock-extended';
|
31 |
|
32 | interface PartyProvider {
|
33 | getPartyType: () => string;
|
34 | getSongs: (type: string) => string[]
|
35 | start: (type: string) => void;
|
36 | }
|
37 |
|
38 | describe('Party Tests', () => {
|
39 | test('Mock out an interface', () => {
|
40 | const mock = mock<PartyProvider>();
|
41 | mock.start('disco party');
|
42 |
|
43 | expect(mock.start).toHaveBeenCalledWith('disco party');
|
44 | });
|
45 |
|
46 |
|
47 | test('mock out a return type', () => {
|
48 | const mock = mock<PartyProvider>();
|
49 | mock.getPartyType.mockReturnValue('west coast party');
|
50 |
|
51 | expect(mock.getPartyType()).toBe('west coast party');
|
52 | });
|
53 |
|
54 | test('throwing an error if we forget to specify the return value')
|
55 | const mock = mock<PartyProvider>(
|
56 | {},
|
57 | {
|
58 | fallbackMockImplementation: () => {
|
59 | throw new Error('not mocked');
|
60 | },
|
61 | }
|
62 | );
|
63 |
|
64 | expect(() => mock.getPartyType()).toThrowError('not mocked');
|
65 | });
|
66 | ```
|
67 |
|
68 | ## Assigning Mocks with a Type
|
69 |
|
70 | If you wish to assign a mock to a variable that requires a type in your test, then you should use the MockProxy<> type
|
71 | given that this will provide the apis for calledWith() and other built-in jest types for providing test functionality.
|
72 |
|
73 | ```ts
|
74 | import { MockProxy, mock } from 'jest-mock-extended';
|
75 |
|
76 | describe('test', () => {
|
77 | let myMock: MockProxy<MyInterface>;
|
78 |
|
79 | beforeEach(() => {
|
80 | myMock = mock<MyInterface>();
|
81 | })
|
82 |
|
83 | test(() => {
|
84 | myMock.calledWith(1).mockReturnValue(2);
|
85 | ...
|
86 | })
|
87 | });
|
88 |
|
89 | ```
|
90 |
|
91 | ## calledWith() Extension
|
92 |
|
93 | ```jest-mock-extended``` allows for invocation matching expectations. Types of arguments, even when using matchers are type checked.
|
94 |
|
95 | ```ts
|
96 | const provider = mock<PartyProvider>();
|
97 | provider.getSongs.calledWith('disco party').mockReturnValue(['Dance the night away', 'Stayin Alive']);
|
98 | expect(provider.getSongs('disco party')).toEqual(['Dance the night away', 'Stayin Alive']);
|
99 |
|
100 | // Matchers
|
101 | provider.getSongs.calledWith(any()).mockReturnValue(['Saw her standing there']);
|
102 | provider.getSongs.calledWith(anyString()).mockReturnValue(['Saw her standing there']);
|
103 |
|
104 | ```
|
105 | You can also use ```mockFn()``` to create a ```jest.fn()``` with the calledWith extension:
|
106 |
|
107 | ```ts
|
108 | type MyFn = (x: number, y: number) => Promise<string>;
|
109 | const fn = mockFn<MyFn>();
|
110 | fn.calledWith(1, 2).mockReturnValue('str');
|
111 | ```
|
112 |
|
113 | ## Clearing / Resetting Mocks
|
114 |
|
115 | ```jest-mock-extended``` exposes a mockClear and mockReset for resetting or clearing mocks with the same
|
116 | functionality as ```jest.fn()```.
|
117 |
|
118 | ```ts
|
119 | import { mock, mockClear, mockReset } from 'jest-mock-extended';
|
120 |
|
121 | describe('test', () => {
|
122 | const mock: UserService = mock<UserService>();
|
123 |
|
124 | beforeEach(() => {
|
125 | mockReset(mock); // or mockClear(mock)
|
126 | });
|
127 | ...
|
128 | })
|
129 | ```
|
130 |
|
131 | ## Deep mocks
|
132 |
|
133 | If your class has objects returns from methods that you would also like to mock, you can use ```mockDeep``` in
|
134 | replacement for mock.
|
135 |
|
136 | ```ts
|
137 | import { mockDeep } from 'jest-mock-extended';
|
138 |
|
139 | const mockObj: DeepMockProxy<Test1> = mockDeep<Test1>();
|
140 | mockObj.deepProp.getNumber.calledWith(1).mockReturnValue(4);
|
141 | expect(mockObj.deepProp.getNumber(1)).toBe(4);
|
142 | ```
|
143 | if you also need support for properties on functions, you can pass in an option to enable this
|
144 |
|
145 | ```ts
|
146 | import { mockDeep } from 'jest-mock-extended';
|
147 |
|
148 | const mockObj: DeepMockProxy<Test1> = mockDeep<Test1>({ funcPropSupport: true });
|
149 | mockObj.deepProp.calledWith(1).mockReturnValue(3)
|
150 | mockObj.deepProp.getNumber.calledWith(1).mockReturnValue(4);
|
151 |
|
152 | expect(mockObj.deepProp(1)).toBe(3);
|
153 | expect(mockObj.deepProp.getNumber(1)).toBe(4);
|
154 | ```
|
155 |
|
156 | Can can provide a fallback mock implementation used if you do not define a return value using `calledWith`.
|
157 |
|
158 | ```ts
|
159 | import { mockDeep } from 'jest-mock-extended';
|
160 | const mockObj = mockDeep<Test1>({
|
161 | fallbackMockImplementation: () => {
|
162 | throw new Error('please add expected return value using calledWith');
|
163 | },
|
164 | });
|
165 | expect(() => mockObj.getNumber()).toThrowError('not mocked');
|
166 | ```
|
167 |
|
168 |
|
169 | ## Available Matchers
|
170 |
|
171 |
|
172 | | Matcher | Description |
|
173 | |-----------------------|-----------------------------------------------------------------------|
|
174 | |any() | Matches any arg of any type. |
|
175 | |anyBoolean() | Matches any boolean (true or false) |
|
176 | |anyString() | Matches any string including empty string |
|
177 | |anyNumber() | Matches any number that is not NaN |
|
178 | |anyFunction() | Matches any function |
|
179 | |anyObject() | Matches any object (typeof m === 'object') and is not null |
|
180 | |anyArray() | Matches any array |
|
181 | |anyMap() | Matches any Map |
|
182 | |anySet() | Matches any Set |
|
183 | |isA(class) | e.g isA(DiscoPartyProvider) |
|
184 | |includes('value') | Checks if value is in the argument array |
|
185 | |containsKey('key') | Checks if the key exists in the object |
|
186 | |containsValue('value') | Checks if the value exists in an object |
|
187 | |has('value') | checks if the value exists in a Set |
|
188 | |notNull() | value !== null |
|
189 | |notUndefined() | value !== undefined |
|
190 | |notEmpty() | value !== undefined && value !== null && value !== '' |
|
191 | |captor() | Used to capture an arg - alternative to mock.calls[0][0] |
|
192 |
|
193 | ## Writing a Custom Matcher
|
194 |
|
195 | Custom matchers can be written using a ```MatcherCreator```
|
196 |
|
197 | ```ts
|
198 | import { MatcherCreator, Matcher } from 'jest-mock-extended';
|
199 |
|
200 | // expectedValue is optional
|
201 | export const myMatcher: MatcherCreator<MyType> = (expectedValue) => new Matcher((actualValue) => {
|
202 | return (expectedValue === actualValue && actualValue.isSpecial);
|
203 | });
|
204 | ```
|
205 |
|
206 | By default, the expected value and actual value are the same type. In the case where you need to type the expected value
|
207 | differently than the actual value, you can use the optional 2 generic parameter:
|
208 |
|
209 | ```ts
|
210 | import { MatcherCreator, Matcher } from 'jest-mock-extended';
|
211 |
|
212 | // expectedValue is optional
|
213 | export const myMatcher: MatcherCreator<string[], string> = (expectedValue) => new Matcher((actualValue) => {
|
214 | return (actualValue.includes(expectedValue));
|
215 | });
|
216 | ```
|