UNPKG

@helpscout/cyan

Version:

Cypress-like Testing for React + JSDOM

275 lines (195 loc) 6.79 kB
--- id: mock-promises title: Mock Promises sidebar_label: Mock Promises hide_title: true --- # Mock Promises Cyan provides some ways to make [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) easier to work with within Jest. These mock Promises give you finer grain control, allowing you to fire Promises in a **synchronous** and **instant** manner! #### Example In the following example, we have an `AsyncComponent` that uses a Promise based fetch method which eventually allows the component to render it's content (once resolved). ```jsx // AsyncComponent.js import React from 'react' import { someFetchMethod } from '../api' class AsyncComponent extends React.Component { state = { isLoaded: false, } componentDidMount() { someFetchMethod().then(() => { this.setState({ isLoaded: true, }) }) } render() { return this.state.isLoaded ? <div>Hello</div> : null } } ``` In our test, we can set up mock Promises using `cy.useFakePromises()`, similar to Jest's [Timer Mocks](https://jestjs.io/docs/en/timer-mocks). This wraps all following Promises with our special mock Promise implementation. ```jsx // __tests__/AsyncComponent.test.js import React from 'react' import { cy } from '@helpscout/cyan' import AsyncComponent from '../AsyncComponent' test('It can load', () => { // Enable the mocking of Promises with cy.useFakePromises() cy.useFakePromises() // Render your Component cy.render(<AsyncComponent />) // By default, the (mocked) Promises do not run immediately. // To run it, you can use the cy.runNextPromise() method. cy.runNextPromise() // Great! Our Promise has executed // Let's make sure that our content rendered. expect(cy.get('div').text()).contains('Hello') }) ``` The original Promise implementation is automatically restored [after each](https://jestjs.io/docs/en/setup-teardown) test run. Note: Cyan currently **does not** support `async`/`await` Promise implementations. --- ## useFakePromises `Function(): void` Implements Cyan's mock Promise for the next Jest test run. If you want all of your Promises to be mocked, add it to a `beforeEach` or `beforeAll` hook. #### Example ```js // Promises will be mocked for every test run. beforeEach(() => { cy.useFakePromises() }) ``` Mock Promises are added to a special queue. Once there, they can be run immediately using [`cy.runPromisesImmediately()`](#runpromisesimmediately) or sequentially using [`cy.runNextPromise()`](#runnextpromise). ## clearFakePromises `Function(): void` Restores Promises back to the original implementation. This method is automatically run for you if you use `cy.setupTests()`. #### Example ```js test('My async test', () => { // Start mocking Promises. cy.useFakePromises() // Execute + assert against your function/component // ... // Restore the actual Promise implementation cy.clearFakePromises() // Continue to execute + assert against your // function/component, but, with real Promises. }) ``` ## runPromisesImmediately `Function(): void` Forces all following mock Promises to be fired immediately. If you want all of your Promises to fire immediately, add it to a `beforeEach` or `beforeAll` hook. #### Example ```js beforeEach(() => { // Promises will be mocked for every test run. cy.useFakePromises() // Promises will also fire immediately. cy.runPromisesImmediately() }) ``` In the following example, let's pretend we have a `MultiPromiseComponent` component that runs 3 chained Promises when it renders. ```js test('My multi-promise test', () => { // Start mocking Promises. cy.useFakePromises() cy.runPromisesImmediately() cy.render(<MultiPromiseComponent />) // All 3 Promises would have run on render. }) ``` ## runNextPromise `Function(): void` Runs the next Promise from the mock Promise queue. #### Example Let's pretend we have a `MultiPromiseComponent` component that runs 3 chained Promises when it renders. ```js test('My multi-promise test', () => { // Start mocking Promises. cy.useFakePromises() cy.render(<MultiPromiseComponent />) // Runs the first (1 of 3) Promise cy.runNextPromise() // Runs the second (2 of 3) Promise cy.runNextPromise() // Runs the third (3 of 3) Promise cy.runNextPromise() }) ``` ## runAllPromises `Function(): void` Runs all of the following Promise from the mock Promise queue. #### Example Let's pretend we have a `MultiPromiseComponent` component that runs 3 chained Promises when it renders. ```js test('My multi-promise test', () => { // Start mocking Promises. cy.useFakePromises() cy.render(<MultiPromiseComponent />) // Runs all (3) of the following Promise cy.runAllPromises() }) ``` ## forceAllPromisesToResolve `Function(callback?: Function<any>): void` Forces all following mock Promises to `.resolve()`. This method accepts a callback, allowing you to transform the result of the `.resolve()` result. #### Arguments | Prop | Type | Required | Description | | -------- | ---------- | -------- | ------------------------------------- | | callback | `Function` | | Can transform the `resolve()` result. | #### Example ```js test('My multi-promise test', () => { // Start mocking Promises. cy.useFakePromises() // Intercept and transform the `.resolve()` result const resolve = result => { return { status: 200, data: { message: 'Hello!', result, }, } } // Make all of the following promises `.resolve()` cy.forceAllPromisesToResolve(resolve) cy.render(<FetchedComponent />) // Run the next Promise cy.runNextPromise() }) ``` ## forceAllPromisesToReject `Function(callback?: Function<any>): void` Forces all following mock Promises to `.reject()`. This method accepts a callback, allowing you to transform the reason of the `.reject()` result. #### Arguments | Prop | Type | Required | Description | | -------- | ---------- | -------- | ------------------------------------ | | callback | `Function` | | Can transform the `reject()` reason. | #### Example ```js test('My multi-promise test', () => { // Start mocking Promises. cy.useFakePromises() // Intercept and transform the `.reject()` result const reject = reason => { return { status: 200, data: { message: 'Hello!', reason, }, } } // Make all of the following promises `.reject()` cy.forceAllPromisesToReject(reject) cy.render(<FetchedComponent />) // Run the next Promise cy.runNextPromise() }) ``` ## See Also - [Mock Timers](./mock-timers.md) - [Installation](../introduction/installation.md)