diff --git a/_reference/Jest CheatSheet.md b/_reference/Jest CheatSheet.md new file mode 100644 index 000000000..5ee12be48 --- /dev/null +++ b/_reference/Jest CheatSheet.md @@ -0,0 +1,564 @@ +
+ + + +

Jest cheat sheet

+ +
+ +_I recommend [Mrm](https://github.com/sapegin/mrm-tasks/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._ + + + + + + + +- [Test structure](#test-structure) +- [Matchers](#matchers) + - [Basic matchers](#basic-matchers) + - [Truthiness](#truthiness) + - [Numbers](#numbers) + - [Strings](#strings) + - [Arrays](#arrays) + - [Objects](#objects) + - [Exceptions](#exceptions) + - [Snapshots](#snapshots) + - [Mock functions](#mock-functions) + - [Misc](#misc) + - [Promise matchers (Jest 20+)](#promise-matchers-jest-20) +- [Async tests](#async-tests) + - [async/await](#asyncawait) + - [Promises](#promises) + - [done() callback](#done-callback) +- [Mocks](#mocks) + - [Mock functions](#mock-functions-1) + - [Mock modules using jest.mock method](#mock-modules-using-jestmock-method) + - [Mock modules using a mock file](#mock-modules-using-a-mock-file) + - [Mock object methods](#mock-object-methods) + - [Mock getters and setters (Jest 22.1.0+)](#mock-getters-and-setters-jest-2210) + - [Mock getters and setters](#mock-getters-and-setters) + - [Clearing and restoring mocks](#clearing-and-restoring-mocks) + - [Accessing the original module when using mocks](#accessing-the-original-module-when-using-mocks) + - [Timer mocks](#timer-mocks) +- [Data-driven tests (Jest 23+)](#data-driven-tests-jest-23) +- [Skipping tests](#skipping-tests) +- [Testing modules with side effects](#testing-modules-with-side-effects) +- [Usage with Babel and TypeScript](#usage-with-babel-and-typescript) +- [Resources](#resources) +- [You may also like](#you-may-also-like) +- [Contributing](#contributing) +- [Sponsoring](#sponsoring) +- [Author and license](#author-and-license) + + + +## Test structure + +```js +describe('makePoniesPink', () => { + beforeAll(() => { + /* Runs before all tests */ + }) + afterAll(() => { + /* Runs after all tests */ + }) + beforeEach(() => { + /* Runs before each test */ + }) + afterEach(() => { + /* Runs after each test */ + }) + + test('make each pony pink', () => { + const actual = fn(['Alice', 'Bob', 'Eve']) + expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink Eve']) + }) +}) +``` + +## Matchers + +[Using matchers](http://jestjs.io/docs/en/using-matchers), [matchers docs](https://facebook.github.io/jest/docs/expect.html) + +### Basic matchers + +```js +expect(42).toBe(42) // Strict equality (===) +expect(42).not.toBe(3) // Strict equality (!==) +expect([1, 2]).toEqual([1, 2]) // Deep equality +expect({ a: undefined, b: 2 }).toEqual({ b: 2 }) // Deep equality +expect({ a: undefined, b: 2 }).not.toStrictEqual({ b: 2 }) // Strict equality (Jest 23+) +``` + +### Truthiness + +```js +// Matches anything that an if statement treats as true (not false, 0, '', null, undefined, NaN) +expect('foo').toBeTruthy() +// Matches anything that an if statement treats as false (false, 0, '', null, undefined, NaN) +expect('').toBeFalsy() +// Matches only null +expect(null).toBeNull() +// Matches only undefined +expect(undefined).toBeUndefined() +// The opposite of toBeUndefined +expect(7).toBeDefined() +``` + +### Numbers + +```js +expect(2).toBeGreaterThan(1) +expect(1).toBeGreaterThanOrEqual(1) +expect(1).toBeLessThan(2) +expect(1).toBeLessThanOrEqual(1) +expect(0.2 + 0.1).toBeCloseTo(0.3, 5) +``` + +### Strings + +```js +expect('long string').toMatch('str') +expect('coffee').toMatch(/ff/) +expect('pizza').not.toMatch('coffee') +expect(['pizza', 'coffee']).toEqual([expect.stringContaining('zz'), expect.stringMatching(/ff/)]) +``` + +### Arrays + +```js +expect(['Alice', 'Bob', 'Eve']).toHaveLength(3) +expect(['Alice', 'Bob', 'Eve']).toContain('Alice') +expect([{ a: 1 }, { a: 2 }]).toContainEqual({ a: 1 }) +expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(['Alice', 'Bob'])) +``` + +### Objects + +```js +expect({ a: 1 }).toHaveProperty('a') +expect({ a: 1 }).toHaveProperty('a', 1) +expect({ a: { b: 1 } }).toHaveProperty('a.b') +expect({ a: 1, b: 2 }).toMatchObject({ a: 1 }) +expect({ a: 1, b: 2 }).toMatchObject({ + a: expect.any(Number), + b: expect.any(Number) +}) +expect([{ a: 1 }, { b: 2 }]).toEqual([ + expect.objectContaining({ a: expect.any(Number) }), + expect.anything() +]) +``` + +### Exceptions + +```js +// const fn = () => { throw new Error('Out of cheese!') } +expect(fn).toThrow() +expect(fn).toThrow('Out of cheese') +expect(fn).toThrowErrorMatchingSnapshot() +``` + +
+ Aliases + +- `toThrowError` → `toThrow` +
+ +### Snapshots + +```js +expect(node).toMatchSnapshot() +// Jest 23+ +expect(user).toMatchSnapshot({ + date: expect.any(Date) +}) +expect(user).toMatchInlineSnapshot() +``` + +### Mock functions + +```js +// const fn = jest.fn() +// const fn = jest.fn().mockName('Unicorn') -- named mock, Jest 22+ +expect(fn).toBeCalled() // Function was called +expect(fn).not.toBeCalled() // Function was *not* called +expect(fn).toHaveBeenCalledTimes(1) // Function was called only once +expect(fn).toBeCalledWith(arg1, arg2) // Any of calls was with these arguments +expect(fn).toHaveBeenLastCalledWith(arg1, arg2) // Last call was with these arguments +expect(fn).toHaveBeenNthCalledWith(args) // Nth call was with these arguments (Jest 23+) +expect(fn).toHaveReturnedTimes(2) // Function was returned without throwing an error (Jest 23+) +expect(fn).toHaveReturnedWith(value) // Function returned a value (Jest 23+) +expect(fn).toHaveLastReturnedWith(value) // Last function call returned a value (Jest 23+) +expect(fn).toHaveNthReturnedWith(value) // Nth function call returned a value (Jest 23+) +expect(fn.mock.calls).toEqual([['first', 'call', 'args'], ['second', 'call', 'args']]) // Multiple calls +expect(fn.mock.calls[0][0]).toBe(2) // fn.mock.calls[0][0] — the first argument of the first call +``` + +
+ Aliases + +- `toBeCalled` → `toHaveBeenCalled` +- `toBeCalledWith` → `toHaveBeenCalledWith` +- `lastCalledWith` → `toHaveBeenLastCalledWith` +- `nthCalledWith` → `toHaveBeenNthCalledWith` +- `toReturnTimes` → `toHaveReturnedTimes` +- `toReturnWith` → `toHaveReturnedWith` +- `lastReturnedWith` → `toHaveLastReturnedWith` +- `nthReturnedWith` → `toHaveNthReturnedWith` +
+ +### Misc + +```js +expect(new A()).toBeInstanceOf(A) +expect(() => {}).toEqual(expect.any(Function)) +expect('pizza').toEqual(expect.anything()) +``` + +### Promise matchers (Jest 20+) + +```js +test('resolve to lemon', () => { + expect.assertions(1) + // Make sure to add a return statement + return expect(Promise.resolve('lemon')).resolves.toBe('lemon') + // return expect(Promise.reject('octopus')).rejects.toBeDefined(); +}) +``` + +Or with async/await: + +```js +test('resolve to lemon', async () => { + expect.assertions(2) + await expect(Promise.resolve('lemon')).resolves.toBe('lemon') + await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus') +}) +``` + +[resolves docs](https://facebook.github.io/jest/docs/en/expect.html#resolves) + +## Async tests + +See [more examples](https://facebook.github.io/jest/docs/en/tutorial-async.html) in Jest docs. + +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. + +```js +test('async test', () => { + expect.assertions(3) // Exactly three assertions are called during a test + // OR + expect.hasAssertions() // At least one assertion is called during a test + + // Your async tests +}) +``` + +### async/await + +```js +test('async test', async () => { + expect.assertions(1) + const result = await runAsyncOperation() + expect(result).toBe(true) +}) +``` + +### Promises + +_Return_ a Promise from your test: + +```js +test('async test', () => { + expect.assertions(1) + return runAsyncOperation().then(result => { + expect(result).toBe(true) + }) +}) +``` + +### done() callback + +Wrap your assertions in try/catch block, otherwise Jest will ignore failures: + +```js +test('async test', done => { + expect.assertions(1) + runAsyncOperation() + setTimeout(() => { + try { + const result = getAsyncOperationResult() + expect(result).toBe(true) + done() + } catch (err) { + done.fail(err) + } + }) +}) +``` + +## Mocks + +### Mock functions + +```js +test('call the callback', () => { + const callback = jest.fn() + fn(callback) + expect(callback).toBeCalled() + expect(callback.mock.calls[0][1].baz).toBe('pizza') // Second argument of the first call +}) +``` + +You can also use snapshots: + +```js +test('call the callback', () => { + const callback = jest.fn().mockName('Unicorn') // mockName is available in Jest 22+ + fn(callback) + expect(callback).toMatchSnapshot() + // -> + // [MockFunction Unicorn] { + // "calls": Array [ + // ... +}) +``` + +And pass an implementation to `jest.fn` function: + +```js +const callback = jest.fn(() => true) +``` + +[Mock functions docs](https://facebook.github.io/jest/docs/mock-function-api.html) + +### Mock modules using `jest.mock` method + +```js +jest.mock('lodash/memoize', () => a => a) // The original lodash/memoize should exist +jest.mock('lodash/memoize', () => a => a, { virtual: true }) // The original lodash/memoize isn’t required +``` + +[jest.mock docs](https://facebook.github.io/jest/docs/jest-object.html#jestmockmodulename-factory-options) + +> Note: 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. + +### Mock modules using a mock file + +1. Create a file like `__mocks__/lodash/memoize.js`: + + ```js + module.exports = a => a + ``` + +2. Add to your test: + + ```js + jest.mock('lodash/memoize') + ``` + +> Note: 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. + +[Manual mocks docs](https://facebook.github.io/jest/docs/manual-mocks.html) + +### Mock object methods + +```js +const spy = jest.spyOn(console, 'log').mockImplementation(() => {}) +expect(console.log.mock.calls).toEqual([['dope'], ['nope']]) +spy.mockRestore() +``` + +```js +const spy = jest.spyOn(ajax, 'request').mockImplementation(() => Promise.resolve({ success: true })) +expect(spy).toHaveBeenCalled() +spy.mockRestore() +``` + +### Mock getters and setters (Jest 22.1.0+) + +```js +const location = {} +const getTitle = jest.spyOn(location, 'title', 'get').mockImplementation(() => 'pizza') +const setTitle = jest.spyOn(location, 'title', 'set').mockImplementation(() => {}) +``` + +### Mock getters and setters + +```js +const getTitle = jest.fn(() => 'pizza') +const setTitle = jest.fn() +const location = {} +Object.defineProperty(location, 'title', { + get: getTitle, + set: setTitle +}) +``` + +### Clearing and restoring mocks + +For one mock: + +```js +fn.mockClear() // Clears mock usage date (fn.mock.calls, fn.mock.instances) +fn.mockReset() // Clears and removes any mocked return values or implementations +fn.mockRestore() // Resets and restores the initial implementation +``` + +> Note: `mockRestore` works only with mocks created by `jest.spyOn`. + +For all mocks: + +```js +jest.clearAllMocks() +jest.resetAllMocks() +jest.restoreAllMocks() +``` + +### Accessing the original module when using mocks + +```js +jest.mock('fs') +const fs = require('fs') // Mocked module +const fs = require.requireActual('fs') // Original module +``` + +### Timer mocks + +Write synchronous test for code that uses native timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`). + +```js +// Enable fake timers +jest.useFakeTimers() + +test('kill the time', () => { + const callback = jest.fn() + // Run some code that uses setTimeout or setInterval + const actual = someFunctionThatUseTimers(callback) + // Fast-forward until all timers have been executed + jest.runAllTimers() + // Check the results synchronously + expect(callback).toHaveBeenCalledTimes(1) +}) +``` + +Use [jest.runOnlyPendingTimers()](https://jestjs.io/docs/en/timer-mocks#run-pending-timers) for special cases. + +Or adjust timers by time with [advanceTimersByTime()](https://jestjs.io/docs/en/timer-mocks#advance-timers-by-time). + +## Data-driven tests (Jest 23+) + +Run the same test with different data: + +```js +test.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])('.add(%s, %s)', (a, b, expected) => { + expect(a + b).toBe(expected) +}) +``` + +Or the same using template literals: + +```js +test.each` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} +`('returns $expected when $a is added $b', ({ a, b, expected }) => { + expect(a + b).toBe(expected) +}) +``` + +Or on `describe` level: + +```js +describe.each([['mobile'], ['tablet'], ['desktop']])('checkout flow on %s', (viewport) => { + test('displays success page', () => { + // + }) +}) +``` + +[describe.each() docs](https://jestjs.io/docs/en/api.html#describeeachtablename-fn-timeout), [test.each() docs](https://jestjs.io/docs/en/api.html#testeachtablename-fn-timeout), + +## Skipping tests + +Don’t run these tests: + +```js +describe.skip('makePoniesPink'... +tests.skip('make each pony pink'... +``` + +Run only these tests: + +```js +describe.only('makePoniesPink'... +tests.only('make each pony pink'... +``` + +## Testing modules with side effects + +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: + +```js +const modulePath = '../module-to-test' + +afterEach(() => { + jest.resetModules() +}) + +test('first test', () => { + // Prepare conditions for the first test + const result = require(modulePath) + expect(result).toMatchSnapshot() +}) + +test('second text', () => { + // Prepare conditions for the second test + const fn = () => require(modulePath) + expect(fn).toThrow() +}) +``` + +## Usage with Babel and TypeScript + +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. + +## Resources + +- [Jest site](https://facebook.github.io/jest/) +- [Testing React components with Jest and Enzyme](http://blog.sapegin.me/all/react-jest) by Artem Sapegin +- [React Testing Examples](https://react-testing-examples.com/) +- [Testing React Applications](https://youtu.be/59Ndb3YkLKA) by Max Stoiber +- [Effective Snapshot Testing](https://blog.kentcdodds.com/effective-snapshot-testing-e0d1a2c28eca) by Kent C. Dodds +- [Migrating to Jest](https://medium.com/@kentcdodds/migrating-to-jest-881f75366e7e#.pc4s5ut6z) by Kent C. Dodds +- [Migrating AVA to Jest](http://browniefed.com/blog/migrating-ava-to-jest/) by Jason Brown +- [How to Test React and MobX with Jest](https://semaphoreci.com/community/tutorials/how-to-test-react-and-mobx-with-jest) by Will Stern +- [Testing React Intl components with Jest and Enzyme](https://medium.com/@sapegin/testing-react-intl-components-with-jest-and-enzyme-f9d43d9c923e) by Artem Sapegin +- [Testing with Jest: 15 Awesome Tips and Tricks](https://medium.com/@stipsan/testing-with-jest-15-awesome-tips-and-tricks-42150ec4c262) by Stian Didriksen +- 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/) + +--- + +## You may also like + +- [Opinionated list of React components](https://github.com/sapegin/react-components) + +## Contributing + +Improvements are welcome! Open an issue or send a pull request. + +## Sponsoring + +This software has been developed with lots of coffee, buy me one more cup to keep it going. + +Buy Me A Coffee + +## Author and license + +[Artem Sapegin](http://sapegin.me/), a frontend engineer at [Omio](https://omio.com/) and the creator of [React Styleguidist](https://react-styleguidist.js.org/). I also write about frontend at [my blog](https://blog.sapegin.me/). + +CC0 1.0 Universal license, see the included [License.md](/License.md) file. diff --git a/server.js b/server.js index 1dfb16805..8f9070773 100644 --- a/server.js +++ b/server.js @@ -9,7 +9,7 @@ if (process.env.NODE_ENV !== "production") require("dotenv").config(); const app = express(); const port = process.env.PORT || 5000; -app.use(compression); +app.use(compression()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(enforce.HTTPS({ trustProtoHeader: true }));