├── .github └── FUNDING.yml ├── License.md └── Readme.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: sapegin 2 | ko_fi: sapegin 3 | custom: https://www.buymeacoffee.com/sapegin 4 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 | 11 | 12 | 13 |

Jest cheat sheet

14 | 15 |
16 | 17 | _Migrating to Vitest? [Check out Vitest cheat sheet](https://github.com/sapegin/vitest-cheat-sheet)._ 18 | 19 | _I recommend [Mrm](https://github.com/sapegin/mrm/tree/master/packages/mrm-task-jest) and [jest-codemods](https://github.com/skovhus/jest-codemods) for single-command Jest installation and easy migration from other frameworks._ 20 | 21 | [![Washing your code. A book on clean code for frontend developers](https://sapegin.me/images/washing-code-github.jpg)](https://sapegin.me/book/) 22 | 23 | ## Table of contents 24 | 25 | 26 | 27 | - [Test structure](#test-structure) 28 | - [Matchers](#matchers) 29 | - [Basic matchers](#basic-matchers) 30 | - [Truthiness](#truthiness) 31 | - [Numbers](#numbers) 32 | - [Strings](#strings) 33 | - [Arrays](#arrays) 34 | - [Objects](#objects) 35 | - [Exceptions](#exceptions) 36 | - [Snapshots](#snapshots) 37 | - [Mock functions](#mock-functions) 38 | - [Misc](#misc) 39 | - [Promise matchers (Jest 20+)](#promise-matchers-jest-20) 40 | - [Async tests](#async-tests) 41 | - [async/await](#asyncawait) 42 | - [Promises](#promises) 43 | - [done() callback](#done-callback) 44 | - [Mocks](#mocks) 45 | - [Mock functions](#mock-functions-1) 46 | - [Returning, resolving and rejecting values](#returning-resolving-and-rejecting-values) 47 | - [Mock modules using `jest.mock` method](#mock-modules-using-jestmock-method) 48 | - [Mock modules using a mock file](#mock-modules-using-a-mock-file) 49 | - [Mock object methods](#mock-object-methods) 50 | - [Mock getters and setters (Jest 22.1.0+)](#mock-getters-and-setters-jest-2210) 51 | - [Mock getters and setters](#mock-getters-and-setters) 52 | - [Clearing and restoring mocks](#clearing-and-restoring-mocks) 53 | - [Accessing the original module when using mocks](#accessing-the-original-module-when-using-mocks) 54 | - [Timer mocks](#timer-mocks) 55 | - [Data-driven tests (Jest 23+)](#data-driven-tests-jest-23) 56 | - [Skipping tests](#skipping-tests) 57 | - [Testing modules with side effects](#testing-modules-with-side-effects) 58 | - [Usage with Babel and TypeScript](#usage-with-babel-and-typescript) 59 | - [Resources](#resources) 60 | - [You may also like](#you-may-also-like) 61 | - [Contributing](#contributing) 62 | - [Sponsoring](#sponsoring) 63 | - [Author and license](#author-and-license) 64 | 65 | 66 | 67 | ## Test structure 68 | 69 | ```js 70 | describe('makePoniesPink', () => { 71 | beforeAll(() => { 72 | /* Runs before all tests */ 73 | }) 74 | afterAll(() => { 75 | /* Runs after all tests */ 76 | }) 77 | beforeEach(() => { 78 | /* Runs before each test */ 79 | }) 80 | afterEach(() => { 81 | /* Runs after each test */ 82 | }) 83 | 84 | test('make each pony pink', () => { 85 | const actual = fn(['Alice', 'Bob', 'Eve']) 86 | expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink Eve']) 87 | }) 88 | }) 89 | ``` 90 | 91 | ## Matchers 92 | 93 | [Using matchers](http://jestjs.io/docs/en/using-matchers), [matchers docs](https://jestjs.io/docs/en/expect) 94 | 95 | ### Basic matchers 96 | 97 | ```js 98 | expect(42).toBe(42) // Strict equality (===) 99 | expect(42).not.toBe(3) // Strict equality (!==) 100 | expect([1, 2]).toEqual([1, 2]) // Deep equality 101 | expect({ a: undefined, b: 2 }).toEqual({ b: 2 }) // Deep equality 102 | expect({ a: undefined, b: 2 }).not.toStrictEqual({ b: 2 }) // Strict equality (Jest 23+) 103 | ``` 104 | 105 | ### Truthiness 106 | 107 | ```js 108 | // Matches anything that an if statement treats as true (true, 1, 'hello', {}, [], 5.3) 109 | expect('foo').toBeTruthy() 110 | // Matches anything that an if statement treats as false (false, 0, '', null, undefined, NaN) 111 | expect('').toBeFalsy() 112 | // Matches only null 113 | expect(null).toBeNull() 114 | // Matches only undefined 115 | expect(undefined).toBeUndefined() 116 | // The opposite of toBeUndefined 117 | expect(7).toBeDefined() 118 | // Matches true or false 119 | expect(true).toEqual(expect.any(Boolean)) 120 | ``` 121 | 122 | ### Numbers 123 | 124 | ```js 125 | expect(2).toBeGreaterThan(1) 126 | expect(1).toBeGreaterThanOrEqual(1) 127 | expect(1).toBeLessThan(2) 128 | expect(1).toBeLessThanOrEqual(1) 129 | expect(0.2 + 0.1).toBeCloseTo(0.3, 5) 130 | expect(NaN).toEqual(expect.any(Number)) 131 | ``` 132 | 133 | ### Strings 134 | 135 | ```js 136 | expect('long string').toMatch('str') 137 | expect('string').toEqual(expect.any(String)) 138 | expect('coffee').toMatch(/ff/) 139 | expect('pizza').not.toMatch('coffee') 140 | expect(['pizza', 'coffee']).toEqual([expect.stringContaining('zz'), expect.stringMatching(/ff/)]) 141 | ``` 142 | 143 | ### Arrays 144 | 145 | ```js 146 | expect([]).toEqual(expect.any(Array)) 147 | expect(['Alice', 'Bob', 'Eve']).toHaveLength(3) 148 | expect(['Alice', 'Bob', 'Eve']).toContain('Alice') 149 | expect([{ a: 1 }, { a: 2 }]).toContainEqual({ a: 1 }) 150 | expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(['Alice', 'Bob'])) 151 | ``` 152 | 153 | ### Objects 154 | 155 | ```js 156 | expect({ a: 1 }).toHaveProperty('a') 157 | expect({ a: 1 }).toHaveProperty('a', 1) 158 | expect({ a: { b: 1 } }).toHaveProperty('a.b') 159 | expect({ a: 1, b: 2 }).toMatchObject({ a: 1 }) 160 | expect({ a: 1, b: 2 }).toMatchObject({ 161 | a: expect.any(Number), 162 | b: expect.any(Number), 163 | }) 164 | expect([{ a: 1 }, { b: 2 }]).toEqual([ 165 | expect.objectContaining({ a: expect.any(Number) }), 166 | expect.anything(), 167 | ]) 168 | ``` 169 | 170 | ### Exceptions 171 | 172 | ```js 173 | // const fn = () => { throw new Error('Out of cheese!') } 174 | expect(fn).toThrow() 175 | expect(fn).toThrow('Out of cheese') 176 | expect(fn).toThrowErrorMatchingSnapshot() 177 | ``` 178 | 179 |
180 | Aliases 181 | 182 | - `toThrowError` → `toThrow` 183 |
184 | 185 | ### Snapshots 186 | 187 | ```js 188 | expect(node).toMatchSnapshot() 189 | // Jest 23+ 190 | expect(user).toMatchSnapshot({ 191 | date: expect.any(Date), 192 | }) 193 | expect(user).toMatchInlineSnapshot() 194 | ``` 195 | 196 | ### Mock functions 197 | 198 | ```js 199 | // const fn = jest.fn() 200 | // const fn = jest.fn().mockName('Unicorn') -- named mock, Jest 22+ 201 | expect(fn).toBeCalled() // Function was called 202 | expect(fn).not.toBeCalled() // Function was *not* called 203 | expect(fn).toHaveBeenCalledTimes(1) // Function was called only once 204 | expect(fn).toBeCalledWith(arg1, arg2) // Any of calls was with these arguments 205 | expect(fn).toHaveBeenLastCalledWith(arg1, arg2) // Last call was with these arguments 206 | expect(fn).toHaveBeenNthCalledWith(callNumber, args) // Nth call was with these arguments (Jest 23+) 207 | expect(fn).toHaveReturnedTimes(2) // Function was returned without throwing an error (Jest 23+) 208 | expect(fn).toHaveReturnedWith(value) // Function returned a value (Jest 23+) 209 | expect(fn).toHaveLastReturnedWith(value) // Last function call returned a value (Jest 23+) 210 | expect(fn).toHaveNthReturnedWith(value) // Nth function call returned a value (Jest 23+) 211 | expect(fn.mock.calls).toEqual([ 212 | ['first', 'call', 'args'], 213 | ['second', 'call', 'args'], 214 | ]) // Multiple calls 215 | expect(fn.mock.calls[0][0]).toBe(2) // fn.mock.calls[0][0] — the first argument of the first call 216 | ``` 217 | 218 |
219 | Aliases 220 | 221 | - `toBeCalled` → `toHaveBeenCalled` 222 | - `toBeCalledWith` → `toHaveBeenCalledWith` 223 | - `lastCalledWith` → `toHaveBeenLastCalledWith` 224 | - `nthCalledWith` → `toHaveBeenNthCalledWith` 225 | - `toReturnTimes` → `toHaveReturnedTimes` 226 | - `toReturnWith` → `toHaveReturnedWith` 227 | - `lastReturnedWith` → `toHaveLastReturnedWith` 228 | - `nthReturnedWith` → `toHaveNthReturnedWith` 229 |
230 | 231 | ### Misc 232 | 233 | ```js 234 | expect(new A()).toBeInstanceOf(A) 235 | expect(() => {}).toEqual(expect.any(Function)) 236 | expect('pizza').toEqual(expect.anything()) 237 | ``` 238 | 239 | ### Promise matchers (Jest 20+) 240 | 241 | ```js 242 | test('resolve to lemon', () => { 243 | expect.assertions(1) 244 | // Make sure to add a return statement 245 | return expect(Promise.resolve('lemon')).resolves.toBe('lemon') 246 | return expect(Promise.reject('octopus')).rejects.toBeDefined() 247 | return expect(Promise.reject(Error('pizza'))).rejects.toThrow() 248 | }) 249 | ``` 250 | 251 | Or with async/await: 252 | 253 | ```js 254 | test('resolve to lemon', async () => { 255 | expect.assertions(2) 256 | await expect(Promise.resolve('lemon')).resolves.toBe('lemon') 257 | await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus') 258 | }) 259 | ``` 260 | 261 | [resolves docs](https://jestjs.io/docs/en/expect#resolves) 262 | 263 | ## Async tests 264 | 265 | See [more examples](https://jestjs.io/docs/en/tutorial-async) in Jest docs. 266 | 267 | It’s a good practice to specify a number of expected assertions in async tests, so the test will fail if your assertions weren’t called at all. 268 | 269 | ```js 270 | test('async test', () => { 271 | expect.assertions(3) // Exactly three assertions are called during a test 272 | // OR 273 | expect.hasAssertions() // At least one assertion is called during a test 274 | 275 | // Your async tests 276 | }) 277 | ``` 278 | 279 | You can also do this per file, outside any `describe` and `test`: 280 | 281 | ```js 282 | beforeEach(expect.hasAssertions) 283 | ``` 284 | 285 | This will verify the presence of at least one assertion per test case. It also plays nice with more specific `expect.assertions(3)` declarations. 286 | 287 | In addition, you can enforce it globally, across all test files (instead of having to repeat per file) by adding the exact same line into one of the scripts referenced by the `setupFilesAfterEnv` configuration option. (For example, `setupTests.ts` and that is referenced via a `setupFilesAfterEnv: ['/setupTests.ts']` entry in `jest.config.ts`.) 288 | 289 | ### async/await 290 | 291 | ```js 292 | test('async test', async () => { 293 | expect.assertions(1) 294 | const result = await runAsyncOperation() 295 | expect(result).toBe(true) 296 | }) 297 | ``` 298 | 299 | ### Promises 300 | 301 | _Return_ a Promise from your test: 302 | 303 | ```js 304 | test('async test', () => { 305 | expect.assertions(1) 306 | return runAsyncOperation().then((result) => { 307 | expect(result).toBe(true) 308 | }) 309 | }) 310 | ``` 311 | 312 | ### done() callback 313 | 314 | Wrap your assertions in try/catch block, otherwise Jest will ignore failures: 315 | 316 | ```js 317 | test('async test', (done) => { 318 | expect.assertions(1) 319 | runAsyncOperation() 320 | setTimeout(() => { 321 | try { 322 | const result = getAsyncOperationResult() 323 | expect(result).toBe(true) 324 | done() 325 | } catch (err) { 326 | done.fail(err) 327 | } 328 | }) 329 | }) 330 | ``` 331 | 332 | ## Mocks 333 | 334 | ### Mock functions 335 | 336 | ```js 337 | test('call the callback', () => { 338 | const callback = jest.fn() 339 | fn(callback) 340 | expect(callback).toBeCalled() 341 | expect(callback.mock.calls[0][1].baz).toBe('pizza') // Second argument of the first call 342 | // Match the first and the last arguments but ignore the second argument 343 | expect(callback).toHaveBeenLastCalledWith('meal', expect.anything(), 'margarita') 344 | }) 345 | ``` 346 | 347 | You can also use snapshots: 348 | 349 | ```js 350 | test('call the callback', () => { 351 | const callback = jest.fn().mockName('Unicorn') // mockName is available in Jest 22+ 352 | fn(callback) 353 | expect(callback).toMatchSnapshot() 354 | // -> 355 | // [MockFunction Unicorn] { 356 | // "calls": Array [ 357 | // ... 358 | }) 359 | ``` 360 | 361 | And pass an implementation to `jest.fn` function: 362 | 363 | ```js 364 | const callback = jest.fn(() => true) 365 | ``` 366 | 367 | [Mock functions docs](https://jestjs.io/docs/en/mock-function-api) 368 | 369 | ### Returning, resolving and rejecting values 370 | 371 | Your mocks can return values: 372 | 373 | ```js 374 | const callback = jest.fn().mockReturnValue(true) 375 | const callbackOnce = jest.fn().mockReturnValueOnce(true) 376 | ``` 377 | 378 | Or resolve values: 379 | 380 | ```js 381 | const promise = jest.fn().mockResolvedValue(true) 382 | const promiseOnce = jest.fn().mockResolvedValueOnce(true) 383 | ``` 384 | 385 | They can even reject values: 386 | 387 | ```js 388 | const failedPromise = jest.fn().mockRejectedValue('Error') 389 | const failedPromiseOnce = jest.fn().mockRejectedValueOnce('Error') 390 | ``` 391 | 392 | You can even combine these: 393 | 394 | ```js 395 | const callback = jest.fn().mockReturnValueOnce(false).mockReturnValue(true) 396 | 397 | // -> 398 | // call 1: false 399 | // call 2+: true 400 | ``` 401 | 402 | ### Mock modules using `jest.mock` method 403 | 404 | ```js 405 | jest.mock('lodash/memoize', () => (a) => a) // The original lodash/memoize should exist 406 | jest.mock('lodash/memoize', () => (a) => a, { virtual: true }) // The original lodash/memoize isn’t required 407 | ``` 408 | 409 | [jest.mock docs](https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options) 410 | 411 | > [!NOTE] 412 | > When using `babel-jest`, calls to `jest.mock` will automatically be hoisted to the top of the code block. Use `jest.doMock` if you want to explicitly avoid this behavior. 413 | 414 | ### Mock modules using a mock file 415 | 416 | 1. Create a file like `__mocks__/lodash/memoize.js`: 417 | 418 | ```js 419 | module.exports = (a) => a 420 | ``` 421 | 422 | 2. Add to your test: 423 | 424 | ```js 425 | jest.mock('lodash/memoize') 426 | ``` 427 | 428 | > [!NOTE] 429 | > When using `babel-jest`, calls to `jest.mock` will automatically be hoisted to the top of the code block. Use `jest.doMock` if you want to explicitly avoid this behavior. 430 | 431 | [Manual mocks docs](https://jestjs.io/docs/en/manual-mocks) 432 | 433 | ### Mock object methods 434 | 435 | ```js 436 | const spy = jest.spyOn(console, 'log').mockImplementation(() => {}) 437 | expect(console.log.mock.calls).toEqual([['dope'], ['nope']]) 438 | spy.mockRestore() 439 | ``` 440 | 441 | ```js 442 | const spy = jest.spyOn(ajax, 'request').mockImplementation(() => Promise.resolve({ success: true })) 443 | expect(spy).toHaveBeenCalled() 444 | spy.mockRestore() 445 | ``` 446 | 447 | ### Mock getters and setters (Jest 22.1.0+) 448 | 449 | ```js 450 | const location = {} 451 | const getTitle = jest.spyOn(location, 'title', 'get').mockImplementation(() => 'pizza') 452 | const setTitle = jest.spyOn(location, 'title', 'set').mockImplementation(() => {}) 453 | ``` 454 | 455 | ### Mock getters and setters 456 | 457 | ```js 458 | const getTitle = jest.fn(() => 'pizza') 459 | const setTitle = jest.fn() 460 | const location = {} 461 | Object.defineProperty(location, 'title', { 462 | get: getTitle, 463 | set: setTitle, 464 | }) 465 | ``` 466 | 467 | ### Clearing and restoring mocks 468 | 469 | For one mock: 470 | 471 | ```js 472 | fn.mockClear() // Clears mock usage date (fn.mock.calls, fn.mock.instances) 473 | fn.mockReset() // Clears and removes any mocked return values or implementations 474 | fn.mockRestore() // Resets and restores the initial implementation 475 | ``` 476 | 477 | > [!NOTE] 478 | > The `mockRestore` works only with mocks created by `jest.spyOn`. 479 | 480 | For all mocks: 481 | 482 | ```js 483 | jest.clearAllMocks() 484 | jest.resetAllMocks() 485 | jest.restoreAllMocks() 486 | ``` 487 | 488 | ### Accessing the original module when using mocks 489 | 490 | ```js 491 | jest.mock('fs') 492 | const fs = require('fs') // Mocked module 493 | const fs = require.requireActual('fs') // Original module 494 | ``` 495 | 496 | ### Timer mocks 497 | 498 | Write synchronous test for code that uses native timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`). 499 | 500 | ```js 501 | // Enable fake timers 502 | jest.useFakeTimers() 503 | 504 | test('kill the time', () => { 505 | const callback = jest.fn() 506 | 507 | // Run some code that uses setTimeout or setInterval 508 | const actual = someFunctionThatUseTimers(callback) 509 | 510 | // Fast-forward until all timers have been executed 511 | jest.runAllTimers() 512 | 513 | // Check the results synchronously 514 | expect(callback).toHaveBeenCalledTimes(1) 515 | }) 516 | ``` 517 | 518 | Or adjust timers by time with [advanceTimersByTime()](https://jestjs.io/docs/en/timer-mocks#advance-timers-by-time): 519 | 520 | ```js 521 | // Enable fake timers 522 | jest.useFakeTimers() 523 | 524 | test('kill the time', () => { 525 | const callback = jest.fn() 526 | 527 | // Run some code that uses setTimeout or setInterval 528 | const actual = someFunctionThatUseTimers(callback) 529 | 530 | // Fast-forward for 250 ms 531 | jest.advanceTimersByTime(250) 532 | 533 | // Check the results synchronously 534 | expect(callback).toHaveBeenCalledTimes(1) 535 | }) 536 | ``` 537 | 538 | Use [jest.runOnlyPendingTimers()](https://jestjs.io/docs/en/timer-mocks#run-pending-timers) for special cases. 539 | 540 | > [!NOTE] 541 | > You should call `jest.useFakeTimers()` in your test case to use other fake timer methods. 542 | 543 | ## Data-driven tests (Jest 23+) 544 | 545 | Run the same test with different data: 546 | 547 | ```js 548 | test.each([ 549 | [1, 1, 2], 550 | [1, 2, 3], 551 | [2, 1, 3], 552 | ])('.add(%s, %s)', (a, b, expected) => { 553 | expect(a + b).toBe(expected) 554 | }) 555 | ``` 556 | 557 | Or the same using template literals: 558 | 559 | ```js 560 | test.each` 561 | a | b | expected 562 | ${1} | ${1} | ${2} 563 | ${1} | ${2} | ${3} 564 | ${2} | ${1} | ${3} 565 | `('returns $expected when $a is added $b', ({ a, b, expected }) => { 566 | expect(a + b).toBe(expected) 567 | }) 568 | ``` 569 | 570 | Or on `describe` level: 571 | 572 | ```js 573 | describe.each([['mobile'], ['tablet'], ['desktop']])('checkout flow on %s', (viewport) => { 574 | test('displays success page', () => { 575 | // 576 | }) 577 | }) 578 | ``` 579 | 580 | [describe.each() docs](https://jestjs.io/docs/en/api#describeeachtablename-fn-timeout), [test.each() docs](https://jestjs.io/docs/en/api#testeachtablename-fn-timeout), 581 | 582 | ## Skipping tests 583 | 584 | Don’t run these tests: 585 | 586 | ```js 587 | describe.skip('makePoniesPink'... 588 | tests.skip('make each pony pink'... 589 | ``` 590 | 591 | Run only these tests: 592 | 593 | ```js 594 | describe.only('makePoniesPink'... 595 | tests.only('make each pony pink'... 596 | ``` 597 | 598 | ## Testing modules with side effects 599 | 600 | Node.js and Jest will cache modules you `require`. To test modules with side effects you’ll need to reset the module registry between tests: 601 | 602 | ```js 603 | const modulePath = '../module-to-test' 604 | 605 | afterEach(() => { 606 | jest.resetModules() 607 | }) 608 | 609 | test('first test', () => { 610 | // Prepare conditions for the first test 611 | const result = require(modulePath) 612 | expect(result).toMatchSnapshot() 613 | }) 614 | 615 | test('second text', () => { 616 | // Prepare conditions for the second test 617 | const fn = () => require(modulePath) 618 | expect(fn).toThrow() 619 | }) 620 | ``` 621 | 622 | ## Usage with Babel and TypeScript 623 | 624 | Add [babel-jest](https://github.com/facebook/jest/tree/master/packages/babel-jest) or [ts-jest](https://github.com/kulshekhar/ts-jest). Check their docs for installation instructions. 625 | 626 | ## Resources 627 | 628 | - [Jest site](https://facebook.github.io/jest/) 629 | - [Modern React testing, part 1: best practices](https://blog.sapegin.me/all/react-testing-1-best-practices/) by Artem Sapegin 630 | - [Modern React testing, part 2: Jest and Enzyme](https://blog.sapegin.me/all/react-testing-2-jest-and-enzyme/) by Artem Sapegin 631 | - [Modern React testing, part 3: Jest and React Testing Library](https://blog.sapegin.me/all/react-testing-3-jest-and-react-testing-library/) by Artem Sapegin 632 | - [Modern React testing, part 4: Cypress and Cypress Testing Library](https://sapegin.me/blog/react-testing-4-cypress/) by Artem Sapegin 633 | - [Modern React testing, part 5: Playwright](https://sapegin.me/blog/react-testing-5-playwright/) by Artem Sapegin 634 | - [React Testing Examples](https://react-testing-examples.com/) 635 | - [Testing React Applications](https://youtu.be/59Ndb3YkLKA) by Max Stoiber 636 | - [Effective Snapshot Testing](https://blog.kentcdodds.com/effective-snapshot-testing-e0d1a2c28eca) by Kent C. Dodds 637 | - [Migrating to Jest](https://medium.com/@kentcdodds/migrating-to-jest-881f75366e7e#.pc4s5ut6z) by Kent C. Dodds 638 | - [Migrating AVA to Jest](http://browniefed.com/blog/migrating-ava-to-jest/) by Jason Brown 639 | - [How to Test React and MobX with Jest](https://semaphoreci.com/community/tutorials/how-to-test-react-and-mobx-with-jest) by Will Stern 640 | - [Testing React Intl components with Jest and Enzyme](https://medium.com/@sapegin/testing-react-intl-components-with-jest-and-enzyme-f9d43d9c923e) by Artem Sapegin 641 | - [Testing with Jest: 15 Awesome Tips and Tricks](https://medium.com/@stipsan/testing-with-jest-15-awesome-tips-and-tricks-42150ec4c262) by Stian Didriksen 642 | - Taking Advantage of Jest Matchers by Ben McCormick: [Part 1](https://benmccormick.org/2017/08/15/jest-matchers-1/), [Part 2](https://benmccormick.org/2017/09/04/jest-matchers-2/) 643 | 644 | --- 645 | 646 | ## You may also like 647 | 648 | - [Opinionated list of React components](https://github.com/sapegin/react-components) 649 | 650 | ## Contributing 651 | 652 | Improvements are welcome! Open an issue or send a pull request. 653 | 654 | ## Sponsoring 655 | 656 | This software has been developed with lots of coffee, buy me one more cup to keep it going. 657 | 658 | Buy Me A Coffee 659 | 660 | ## Author and license 661 | 662 | [Artem Sapegin](https://sapegin.me/), a frontend engineer at [Stage+](https://www.stage-plus.com) and the creator of [React Styleguidist](https://react-styleguidist.js.org/). I also write about frontend at [my blog](https://sapegin.me/blog/). 663 | 664 | CC0 1.0 Universal license, see the included [License.md](/License.md) file. 665 | --------------------------------------------------------------------------------