---
title: "Experimental Fetch Polyfill"
url: "https://www.cypress.io/blog/experimental-fetch-polyfill"
description: "Let's take an application that makes an Ajax call to the browser to load a list of ... fruits. You can find this awesome and healthy application in Cypress…"
---

# Experimental Fetch Polyfill

June 29, 2020

By Gleb Bahmutov

Let's take an application that makes an Ajax call to the browser to load a list of ... fruits. You can find this awesome and healthy application in [Cypress Example Recipes](https://github.com/cypress-io/cypress-example-recipes?ref=cypress-io.ghost.io#stubbing-and-spying) under name "Stubbing window.fetch".

## Application

Every time you visit an application you get five new fruits

This application presents two challenges to end-to-end tests. First, the data is random - every time the test visits the page `localhost:7080` a new list of fruits is returned, which complicates the assertions. Second, the application shows a loading indicator while the Ajax call continues. How would you test the loading behavior?

The application's code to fetch the list of fruits is short.

```js
fetch('/favorite-fruits')
.then((response) => {
  if (response.ok) {
    return response.json()
  }
  // else throw an Error
})
.then((response) => {
  updateFavoriteFruits(response.length ? response : 'No favorites')
})
```

## Stubbing window.fetch

Let's start by slowing down the `window.fetch` directly. This way we can confirm the loading element is visible, and once the response arrives, the loading indicator disappears. Because Cypress includes spies and stubs from the excellent [Sinon.js](https://sinonjs.org/?ref=cypress-io.ghost.io) library, we can directly reach into the `window` object and stub its `fetch` method.

```js
describe('stubbing', function () {
  it('shows no Response message', () => {
    cy.visit('/', {
      onBeforeLoad (win) {
        cy.stub(win, 'fetch').withArgs('/favorite-fruits')
        .resolves({
          ok: true,
          json: () => [],
        })
      },
    })

    cy.contains('No favorites').should('be.visible')
  })
})
```

The test passes, and we see the stub in the Command Log - the two records are due to the fact that we have created first a general method stub `window.fetch` and then made created an explicit one with `cy.stub(win, 'fetch').withArgs('/favorite-fruits')`. I think is a good practice to create [targeted stubs by giving argument values or types](https://example.cypress.io/commands/spies-stubs-clocks?ref=cypress-io.ghost.io).

Stubbing window.fetch method

## Delaying the response

In the test above we are resolving Sinon method stub with a promise. Because Cypress comes with [Bluebird.js](https://github.com/petkaantonov/bluebird?ref=cypress-io.ghost.io) under [Cypress.Promise](https://on.cypress.io/promise?ref=cypress-io.ghost.io) we can use its rich API when working with promises. For example, we can delay the promise's resolution, simulating a slow server response. This allows us to test the loading indicator.

```js
it('directly stubs window.fetch to test loading indicator', () => {
  // stub the "fetch(/favorite-fruits)" call from the app
  cy.visit('/', {
    onBeforeLoad (win) {
      cy.stub(win, 'fetch').withArgs('/favorite-fruits')
      .resolves(
        // use Bluebird promise bundled with Cypress
        // to resolve after 2000ms
        Cypress.Promise.resolve({
          ok: true,
          json: () => ['Pineapple 🍍'],
        }).delay(2000)
      )
    },
  })

  // at first, the app is showing the loading indicator
  cy.get('.loader').should('be.visible')
  // once the promise is resolved, the loading indicator goes away
  cy.get('.loader').should('not.exist')
  cy.contains('li', 'Pineapple 🍍')
})
```

The test is passing, and we can see the loading indicator in action.

Loading indicator test from returning a delayed promise

## Using network control

Our dear friend [Kent C Dodds](https://kentcdodds.com/?ref=cypress-io.ghost.io) has recently published an essay [Stop mocking fetch](https://kentcdodds.com/blog/stop-mocking-fetch?ref=cypress-io.ghost.io) where he argues that what we have done above is tying the test to the implementation and can miss some bugs. Instead he recommends mocking _the server_ outside the application. We agree with Kent - that's why Cypress includes [network control](https://on.cypress.io/network-requests?ref=cypress-io.ghost.io) right out of the box.

For a long, long, loooong time, the Cypress network control could not "see" `window.fetch` calls and only understood `XMLHttpRequest` Ajax calls. We are in the [process of updating our entire network control layer](https://github.com/cypress-io/cypress/pull/4176?ref=cypress-io.ghost.io) to solve this problem (and many, many other limitations). Follow the full network stubbing PR \[#4176\]([https://github.com/cypress-io/cypress/pull/4176](https://github.com/cypress-io/cypress/pull/4176?ref=cypress-io.ghost.io) ) expected  to land some time in the summer of 2020 that will replace the need for the temporary workaround.

Meanwhile, we have added a quick `fetch` polyfill as an experimental feature in Cypress [v4.9.0](https://on.cypress.io/changelog?ref=cypress-io.ghost.io#4-9-0). By turning this feature on, the Cypress Test Runner will automatically replace `window.fetch` with a [unfetch polyfill](https://github.com/developit/unfetch?ref=cypress-io.ghost.io) built on top of `XMLHttpRequest` object, making these Ajax requests "visible" to the Test Runner. Let us try this, in `cypress.json` configuration file, turn the feature on

```json
{
  "experimentalFetchPolyfill": true
}
```

Instead of `cy.stub` let's use plain [cy.route](https://on.cypress.io/route?ref=cypress-io.ghost.io) before visiting the application.

```js
it('shows loader while fetching fruits', function () {
  // stub the XHR request from the app
  cy.server()
  cy.route({
    url: '/favorite-fruits',
    response: [],
    delay: 1000,
  })

  cy.visit('/')
  cy.get('.loader').should('be.visible')

  // once the network call finishes, the loader goes away
  cy.get('.loader').should('not.exist')
  cy.contains('.favorite-fruits', 'No favorites')
})
```

The test passes and we can see all matching Ajax requests in their own table above the test's commands.

Stubbing window.fetch using Cypress cy.route method

By using [cy.route](https://on.cypress.io/route?ref=cypress-io.ghost.io) we can write tests with network stubs that feel much closer to the real server responses rather than function stubs. For example, let's see how our application handles a server error after a delay.

```js
it('displays error', function () {
  cy.server()
  cy.route({
    url: '/favorite-fruits',
    status: 500,
    response: '',
    delay: 2000,
    headers: {
      'status-text': 'Orchard under maintenance',
    },
  })

  cy.visit('/')

  cy.get('.favorite-fruits')
    .should('have.text', 
      'Failed loading favorite fruits: Orchard under maintenance')
})
```

The test passes - and we did not even need to add any waits, since Cypress has built-in [command retry-ability](https://on.cypress.io/retry-ability?ref=cypress-io.ghost.io)

Testing server error handling

### Limitations

The experimental polyfill is not foolproof.  It does not work with `fetch` calls made from WebWorkers or ServiceWorker for example. It might not work for streaming responses and [canceled XHRs](https://github.com/cypress-io/cypress/issues/7830?ref=cypress-io.ghost.io). That's why we felt the opt-in experimental flag is the best path forward to avoid breaking already application under tests.

Happy Testing!