Coder Social home page Coder Social logo

jest-fetch-mock's Introduction

Jest Fetch Mock

npm downloads Node.js CI

Fetch is the canonical way to do HTTP requests in the browser, and it can be used in other environments such as React Native. Jest Fetch Mock allows you to easily mock your fetch calls and return the response you need to fake the HTTP requests. It's easy to setup and you don't need a library like nock to get going and it uses Jest's built-in support for mocking under the surface. This means that any of the jest.fn() methods are also available. For more information on the jest mock API, check their docs here

It currently supports the mocking with the cross-fetch polyfill, so it supports Node.js and any browser-like runtime.

Contents

Usage

Package Installation

To setup your fetch mock you need to do the following things:

$ npm install --save-dev jest-fetch-mock

Create a setupJest file to setup the mock or add this to an existing setupFile. :

To setup for all tests

//setupJest.js or similar file
require('jest-fetch-mock').enableMocks()

Add the setupFile to your jest config in package.json:

"jest": {
  "automock": false,
  "resetMocks": false,
  "setupFiles": [
    "./setupJest.js"
  ]
}

With this done, you'll have fetch and fetchMock available on the global scope. Fetch will be used as usual by your code and you'll use fetchMock in your tests

Note: the resetMocks Jest configuration default was changed from false to true in Jest 4.0.1. Therefore the Jest configuration of setting it to false is required if the setupJest.js is what calls "enableMocks()" (i.e. in the above suggested setup) otherwise you will need to call "enableMocks()" in a "beforeEach" block.

Default not mocked

If you would like to have the 'fetchMock' available in all tests but not enabled then add fetchMock.dontMock() after the ...enableMocks() line in setupJest.js:

// adds the 'fetchMock' global variable and rewires 'fetch' global to call 'fetchMock' instead of the real implementation
require('jest-fetch-mock').enableMocks()
// changes default behavior of fetchMock to use the real 'fetch' implementation and not mock responses
fetchMock.dontMock()

If you want a single test file to return to the default behavior of mocking all responses, add the following to the test file:

beforeEach(() => {
  // if you have an existing `beforeEach` just add the following line to it
  fetchMock.doMock()
})

To enable mocking for a specific URL only:

beforeEach(() => {
  // if you have an existing `beforeEach` just add the following lines to it
  fetchMock.mockIf(/^https?:\/\/example.com.*$/, async (req) => {
    if (req.url.endsWith('/path1')) {
      return 'some response body'
    } else if (req.url.endsWith('/path2')) {
      return {
        body: 'another response body',
        headers: {
          'X-Some-Response-Header': 'Some header value'
        }
      }
    } else {
      return {
        status: 404,
        body: 'Not Found'
      }
    }
  })
})

If you have changed the default behavior to use the real implementation, you can guarantee the next call to fetch will be mocked by using the mockOnce function:

fetchMock.mockOnce('the next call to fetch will always return this as the body')

This function behaves exactly like fetchMock.once but guarantees the next call to fetch will be mocked even if the default behavior of fetchMock is to use the real implementation. You can safely convert all you fetchMock.once calls to fetchMock.mockOnce without a risk of changing their behavior.

To setup for an individual test

For JavaScript add the following line to the start of your test case (before any other requires)

require('jest-fetch-mock').enableMocks()

For TypeScript/ES6 add the following lines to the start of your test case (before any other imports)

import { enableFetchMocks } from 'jest-fetch-mock'
enableFetchMocks()

TypeScript importing

If you are using TypeScript and receive errors about the fetchMock global not existing, add a global.d.ts file to the root of your project (or add the following line to an existing global file):

import 'jest-fetch-mock'

If you prefer you can also just import the fetchMock in a test case.

import fetchMock from 'jest-fetch-mock'

You may also need to edit your tsconfig.json and add "dom" and/or "es2015" and/or "esnext" to the 'compilerConfig.lib' property

Using with Create-React-App

If you are using Create-React-App (CRA), the code for setupJest.js above should be placed into src/setupTests.js in the root of your project. CRA automatically uses this filename by convention in the Jest configuration it generates. Similarly, changing to your package.json is not required as CRA handles this when generating your Jest configuration.

For Ejected Create React Apps ONLY:

Note: Keep in mind that if you decide to "eject" before creating src/setupTests.js, the resulting package.json file won't contain any reference to it, so you should manually create the property setupTestFrameworkScriptFile in the configuration for Jest, something like the following:

"jest": {
  "setupTestFrameworkScriptFile": "<rootDir>/src/setupTests.js"
 }

API

Mock Responses

  • fetch.mockResponse(bodyOrFunction, init): fetch - Mock all fetch calls
  • fetch.mockResponseOnce(bodyOrFunction, init): fetch - Mock each fetch call independently
  • fetch.once(bodyOrFunction, init): fetch - Alias for mockResponseOnce(bodyOrFunction, init)
  • fetch.mockResponses(...responses): fetch - Mock multiple fetch calls independently
    • Each argument is an array taking [bodyOrFunction, init]
  • fetch.mockReject(errorOrFunction): fetch - Mock all fetch calls, letting them fail directly
  • fetch.mockRejectOnce(errorOrFunction): fetch - Let the next fetch call fail directly
  • fetch.mockAbort(): fetch - Causes all fetch calls to reject with an "Aborted!" error
  • fetch.mockAbortOnce(): fetch - Causes the next fetch call to reject with an "Aborted!" error

Functions

Instead of passing body, it is also possible to pass a function that returns a promise. The promise should resolve with a string or an object containing body and init props

i.e:

fetch.mockResponse(() => callMyApi().then(res => ({ body: 'ok' })))
// OR
fetch.mockResponse(() => callMyApi().then(res => 'ok'))

The function may take an optional "request" parameter of type http.Request:

fetch.mockResponse(req =>
  req.url === 'http://myapi/'
    ? callMyApi().then(res => 'ok')
    : Promise.reject(new Error('bad url'))
)

Note: the request "url" is parsed and then printed using the equivalent of new URL(input).href so it may not match exactly with the URL's passed to fetch if they are not fully qualified. For example, passing "http://foo.com" to fetch will result in the request URL being "http://foo.com/" (note the trailing slash).

The same goes for rejects:

fetch.mockReject(() =>
  doMyAsyncJob().then(res => Promise.reject(res.errorToRaise))
)
// OR
fetch.mockReject(req =>
  req.headers.get('content-type') === 'text/plain'
    ? Promise.reject('invalid content type')
    : doMyAsyncJob().then(res => Promise.reject(res.errorToRaise))
)

Mock utilities

  • fetch.resetMocks() - Clear previously set mocks so they do not bleed into other mocks
  • fetch.enableMocks() - Enable fetch mocking by overriding global.fetch and mocking node-fetch
  • fetch.disableMocks() - Disable fetch mocking and restore default implementation of fetch and/or node-fetch
  • fetch.mock - The mock state for your fetch calls. Make assertions on the arguments given to fetch when called by the functions you are testing. For more information check the Jest docs

For information on the arguments body and init can take, you can look at the MDN docs on the Response Constructor function, which jest-fetch-mock uses under the surface.

https://developer.mozilla.org/en-US/docs/Web/API/Response/Response

Each mocked response or err or will return a Mock Function. You can use methods like .toHaveBeenCalledWith to ensure that the mock function was called with specific arguments. For more methods detail, take a look at this.

Examples

In most of the complicated examples below, I am testing my action creators in Redux, but it doesn't have to be used with Redux.

Simple mock and assert

In this simple example I won't be using any libraries. It is a simple fetch request, in this case to google.com. First we setup the beforeEach callback to reset our mocks. This isn't strictly necessary in this example, but since we will probably be mocking fetch more than once, we need to reset it across our tests to assert on the arguments given to fetch. Make sure the function wrapping your test is marked as async.

Once we've done that we can start to mock our response. We want to give it an object with a data property and a string value of 12345 and wrap it in JSON.stringify to JSONify it. Here we use mockResponseOnce, but we could also use once, which is an alias for a call to mockResponseOnce.

We then call the function that we want to test with the arguments we want to test with. We use await to wait until the response resolves, and then assert we have got the correct data back.

Finally we can assert on the .mock state that Jest provides for us to test what arguments were given to fetch and how many times it was called

//api.js
export function APIRequest(who) {
  if (who === 'google') {
    return fetch('https://google.com').then(res => res.json())
  } else {
    return 'no argument provided'
  }
}
//api.test.js
import { APIRequest } from './api'

describe('testing api', () => {
  beforeEach(() => {
    fetch.resetMocks()
  })

  it('calls google and returns data to me', async () => {
    fetch.mockResponseOnce(JSON.stringify({ data: '12345' }))

    //assert on the response
    const res = await APIRequest('google')
    expect(res.data).toEqual('12345')

    //assert on the times called and arguments given to fetch
    expect(fetch.mock.calls.length).toEqual(1)
    expect(fetch.mock.calls[0][0]).toEqual('https://google.com')
  })
})

Mocking all fetches

In this example I am mocking just one fetch call. Any additional fetch calls in the same function will also have the same mock response. For more complicated functions with multiple fetch calls, you can check out example 3.

import configureMockStore from 'redux-mock-store' // mock store
import thunk from 'redux-thunk'

const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)

import { getAccessToken } from './accessToken'

describe('Access token action creators', () => {
  it('dispatches the correct actions on successful fetch request', () => {
    fetch.mockResponse(JSON.stringify({ access_token: '12345' }))

    const expectedActions = [
      { type: 'SET_ACCESS_TOKEN', token: { access_token: '12345' } }
    ]
    const store = mockStore({ config: { token: '' } })

    return (
      store
        .dispatch(getAccessToken())
        //getAccessToken contains the fetch call
        .then(() => {
          // return of async actions
          expect(store.getActions()).toEqual(expectedActions)
        })
    )
  })
})

Mocking a failed fetch

In this example I am mocking just one fetch call but this time using the mockReject function to simulate a failed request. Any additional fetch calls in the same function will also have the same mock response. For more complicated functions with multiple fetch calls, you can check out example 3.

import configureMockStore from 'redux-mock-store' // mock store
import thunk from 'redux-thunk'

const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)

import { getAccessToken } from './accessToken'

describe('Access token action creators', () => {
  it('dispatches the correct actions on a failed fetch request', () => {
    fetch.mockReject(new Error('fake error message'))

    const expectedActions = [
      { type: 'SET_ACCESS_TOKEN_FAILED', error: { status: 503 } }
    ]
    const store = mockStore({ config: { token: '' } })

    return (
      store
        .dispatch(getAccessToken())
        //getAccessToken contains the fetch call
        .then(() => {
          // return of async actions
          expect(store.getActions()).toEqual(expectedActions)
        })
    )
  })
})

Mocking aborted fetches

Fetches can be mocked to act as if they were aborted during the request. This can be done in 4 ways:

  1. Using `fetch.mockAbort()`
  2. Using `fetch.mockAbortOnce()`
  3. Passing a request (or request init) with a 'signal' to fetch that has been aborted
  4. Passing a request (or request init) with a 'signal' to fetch and a async function to `fetch.mockResponse` or `fetch.mockResponseOnce` that causes the signal to abort before returning the response
describe('Mocking aborts', () => {
  beforeEach(() => {
    fetch.resetMocks()
    fetch.doMock()
    jest.useFakeTimers()
  })
  afterEach(() => {
    jest.useRealTimers()
  })

  it('rejects with an Aborted! Error', () => {
    fetch.mockAbort()
    expect(fetch('/')).rejects.toThrow('Aborted!')
  })
  it('rejects with an Aborted! Error once then mocks with empty response', async () => {
    fetch.mockAbortOnce()
    await expect(fetch('/')).rejects.toThrow('Aborted!')
    await expect(request()).resolves.toEqual('')
  })

  it('throws when passed an already aborted abort signal', () => {
    const c = new AbortController()
    c.abort()
    expect(() => fetch('/', { signal: c.signal })).toThrow('Aborted!')
  })

  it('rejects when aborted before resolved', async () => {
    const c = new AbortController()
    fetch.mockResponse(async () => {
      jest.advanceTimersByTime(60)
      return ''
    })
    setTimeout(() => c.abort(), 50)
    await expect(fetch('/', { signal: c.signal })).rejects.toThrow('Aborted!')
  })
})

Mocking a redirected response

Set the counter option >= 1 in the response init object to mock a redirected response https://developer.mozilla.org/en-US/docs/Web/API/Response/redirected. Note, this will only work in Node.js as it's a feature of node fetch's response class https://github.com/node-fetch/node-fetch/blob/master/src/response.js#L39.

fetchMock.mockResponse("<main></main>", {
  counter: 1,
  status: 200,
  statusText: "ok",
});

Mocking multiple fetches with different responses

In this next example, the store does not yet have a token, so we make a request to get an access token first. This means that we need to mock two different responses, one for each of the fetches. Here we can use fetch.mockResponseOnce or fetch.once to mock the response only once and call it twice. Because we return the mocked function, we can chain this jQuery style. It internally uses Jest's mockImplementationOnce. You can read more about it on the Jest documentation

import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'

const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)

import { getAnimeDetails } from './animeDetails'

describe('Anime details action creators', () => {
  it('dispatches requests for an access token before requesting for animeDetails', () => {
    fetch
      .once(JSON.stringify({ access_token: '12345' }))
      .once(JSON.stringify({ name: 'naruto' }))

    const expectedActions = [
      { type: 'SET_ACCESS_TOKEN', token: { access_token: '12345' } },
      { type: 'SET_ANIME_DETAILS', animeDetails: { name: 'naruto' } }
    ]
    const store = mockStore({ config: { token: null } })

    return (
      store
        .dispatch(getAnimeDetails('21049'))
        //getAnimeDetails contains the 2 fetch calls
        .then(() => {
          // return of async actions
          expect(store.getActions()).toEqual(expectedActions)
        })
    )
  })
})

Mocking multiple fetches with fetch.mockResponses

fetch.mockResponses takes as many arguments as you give it, all of which are arrays representing each Response Object. It will then call the mockImplementationOnce for each response object you give it. This reduces the amount of boilerplate code you need to write. An alternative is to use .once and chain it multiple times if you don't like wrapping each response arguments in a tuple/array.

In this example our actionCreator calls fetch 4 times, once for each season of the year and then concatenates the results into one final array. You'd have to write fetch.mockResponseOnce 4 times to achieve the same thing:

describe('getYear action creator', () => {
  it('dispatches the correct actions on successful getSeason fetch request', () => {
    fetch.mockResponses(
      [
        JSON.stringify([{ name: 'naruto', average_score: 79 }]),
        { status: 200 }
      ],
      [
        JSON.stringify([{ name: 'bleach', average_score: 68 }]),
        { status: 200 }
      ],
      [
        JSON.stringify([{ name: 'one piece', average_score: 80 }]),
        { status: 200 }
      ],
      [
        JSON.stringify([{ name: 'shingeki', average_score: 91 }]),
        { status: 200 }
      ]
    )

    const expectedActions = [
      {
        type: 'FETCH_ANIMELIST_REQUEST'
      },
      {
        type: 'SET_YEAR',
        payload: {
          animes: [
            { name: 'naruto', average_score: 7.9 },
            { name: 'bleach', average_score: 6.8 },
            { name: 'one piece', average_score: 8 },
            { name: 'shingeki', average_score: 9.1 }
          ],
          year: 2016
        }
      },
      {
        type: 'FETCH_ANIMELIST_COMPLETE'
      }
    ]
    const store = mockStore({
      config: {
        token: { access_token: 'wtw45CmyEuh4P621IDVxWkgVr5QwTg3wXEc4Z7Cv' }
      },
      years: []
    })

    return (
      store
        .dispatch(getYear(2016))
        //This calls fetch 4 times, once for each season
        .then(() => {
          // return of async actions
          expect(store.getActions()).toEqual(expectedActions)
        })
    )
  })
})

Reset mocks between tests with fetch.resetMocks

fetch.resetMocks resets the fetch mock to give fresh mock data in between tests. It only resets the fetch calls as to not disturb any other mocked functionality.

describe('getYear action creator', () => {
  beforeEach(() => {
      fetch.resetMocks();
  });
  it('dispatches the correct actions on successful getSeason fetch request', () => {

    fetch.mockResponses(
      [
        JSON.stringify([ {name: 'naruto', average_score: 79} ]), { status: 200}
      ],
      [
        JSON.stringify([ {name: 'bleach', average_score: 68} ]), { status: 200}
      ]
    )

    const expectedActions = [
      {
        type: 'FETCH_ANIMELIST_REQUEST'
      },
      {
        type: 'SET_YEAR',
        payload: {
          animes: [
            {name: 'naruto', average_score: 7.9},
            {name: 'bleach', average_score: 6.8}
          ],
          year: 2016,
        }
      },
      {
        type: 'FETCH_ANIMELIST_COMPLETE'
      }
    ]
    const store = mockStore({
      config: { token: { access_token: 'wtw45CmyEuh4P621IDVxWkgVr5QwTg3wXEc4Z7Cv' }},
      years: []
    })

    return store.dispatch(getYear(2016))
      //This calls fetch 2 times, once for each season
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedActions)
      })
  });
  it('dispatches the correct actions on successful getSeason fetch request', () => {

    fetch.mockResponses(
      [
        JSON.stringify([ {name: 'bleach', average_score: 68} ]), { status: 200}
      ],
      [
        JSON.stringify([ {name: 'one piece', average_score: 80} ]), { status: 200}
      ],
      [
        JSON.stringify([ {name: 'shingeki', average_score: 91} ]), { status: 200}
      ]
    )

    const expectedActions = [
      {
        type: 'FETCH_ANIMELIST_REQUEST'
      },
      {
        type: 'SET_YEAR',
        payload: {
          animes: [
            {name: 'bleach', average_score: 6.8},
            {name: 'one piece', average_score: 8},
            {name: 'shingeki', average_score: 9.1}
          ],
          year: 2016,
        }
      },
      {
        type: 'FETCH_ANIMELIST_COMPLETE'
      }
    ]
    const store = mockStore({
      config: { token: { access_token: 'wtw45CmyEuh4P621IDVxWkgVr5QwTg3wXEc4Z7Cv' }},
      years: []
    })

    return store.dispatch(getYear(2016))
      //This calls fetch 3 times, once for each season
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedActions)
      })
      ,

  })
})

Using fetch.mock to inspect the mock state of each fetch call

fetch.mock by default uses Jest's mocking functions. Therefore you can make assertions on the mock state. In this example we have an arbitrary function that makes a different fetch request based on the argument you pass to it. In our test, we run Jest's beforeEach() and make sure to reset our mock before each it() block as we will make assertions on the arguments we are passing to fetch(). The most uses property is the fetch.mock.calls array. It can give you information on each call, and their arguments which you can use for your expect() calls. Jest also comes with some nice aliases for the most used ones.

// api.js

import 'cross-fetch'

export function APIRequest(who) {
  if (who === 'facebook') {
    return fetch('https://facebook.com')
  } else if (who === 'twitter') {
    return fetch('https://twitter.com')
  } else {
    return fetch('https://google.com')
  }
}
// api.test.js
import { APIRequest } from './api'

describe('testing api', () => {
  beforeEach(() => {
    fetch.resetMocks()
  })

  it('calls google by default', () => {
    fetch.mockResponse(JSON.stringify({ secret_data: '12345' }))
    APIRequest()

    expect(fetch.mock.calls.length).toEqual(1)
    expect(fetch.mock.calls[0][0]).toEqual('https://google.com')
  })

  it('calls facebook', () => {
    fetch.mockResponse(JSON.stringify({ secret_data: '12345' }))
    APIRequest('facebook')

    expect(fetch.mock.calls.length).toEqual(2)
    expect(fetch.mock.calls[0][0]).toEqual(
      'https://facebook.com/someOtherResource'
    )
    expect(fetch.mock.calls[1][0]).toEqual('https://facebook.com')
  })

  it('calls twitter', () => {
    fetch.mockResponse(JSON.stringify({ secret_data: '12345' }))
    APIRequest('twitter')

    expect(fetch).toBeCalled() // alias for expect(fetch.mock.calls.length).toEqual(1);
    expect(fetch).toBeCalledWith('https://twitter.com') // alias for expect(fetch.mock.calls[0][0]).toEqual();
  })
})

Using functions to mock slow servers

By default you will want to have your fetch mock return immediately. However if you have some custom logic that needs to tests for slower servers, you can do this by passing it a function and returning a promise when your function resolves

// api.test.js
import { request } from './api'

describe('testing timeouts', () => {
  it('resolves with function and timeout', async () => {
    fetch.mockResponseOnce(
      () =>
        new Promise(resolve => setTimeout(() => resolve({ body: 'ok' }), 100))
    )
    try {
      const response = await request()
      expect(response).toEqual('ok')
    } catch (e) {
      throw e
    }
  })
})

Conditional Mocking

In some test scenarios, you may want to temporarily disable (or enable) mocking for all requests or the next (or a certain number of) request(s). You may want to only mock fetch requests to some URLs that match a given request path while in others you may want to mock all requests except those matching a given request path. You may even want to conditionally mock based on request headers.

The conditional mock functions cause jest-fetch-mock to pass fetches through to the concrete fetch implementation conditionally. Calling fetch.dontMock, fetch.doMock, fetch.doMockIf or fetch.dontMockIf overrides the default behavior of mocking/not mocking all requests. fetch.dontMockOnce, fetch.doMockOnce, fetch.doMockOnceIf and fetch.dontMockOnceIf only overrides the behavior for the next call to fetch, then returns to the default behavior (either mocking all requests or mocking the requests based on the last call to fetch.dontMock, fetch.doMock, fetch.doMockIf and fetch.dontMockIf).

Calling fetch.resetMocks() will return to the default behavior of mocking all fetches with a text response of empty string.

  • fetch.dontMock() - Change the default behavior to not mock any fetches until fetch.resetMocks() or fetch.doMock() is called
  • fetch.doMock(bodyOrFunction?, responseInit?) - Reverses fetch.dontMock(). This is the default state after fetch.resetMocks()
  • fetch.dontMockOnce() - For the next fetch, do not mock then return to the default behavior for subsequent fetches. Can be chained.
  • fetch.doMockOnce(bodyOrFunction?, responseInit?) or fetch.mockOnce - For the next fetch, mock the response then return to the default behavior for subsequent fetches. Can be chained.
  • fetch.doMockIf(urlOrPredicate, bodyOrFunction?, responseInit?):fetch or fetch.mockIf - causes all fetches to be not be mocked unless they match the given string/RegExp/predicate (i.e. "only mock 'fetch' if the request is for the given URL otherwise, use the real fetch implementation")
  • fetch.dontMockIf(urlOrPredicate, bodyOrFunction?, responseInit?):fetch - causes all fetches to be mocked unless they match the given string/RegExp/predicate (i.e. "don't mock 'fetch' if the request is for the given URL, otherwise mock the request")
  • fetch.doMockOnceIf(urlOrPredicate, bodyOrFunction?, responseInit?):fetch or fetch.mockOnceIf - causes the next fetch to be mocked if it matches the given string/RegExp/predicate. Can be chained. (i.e. "only mock 'fetch' if the next request is for the given URL otherwise, use the default behavior")
  • fetch.dontMockOnceIf(urlOrPredicate):fetch - causes the next fetch to be not be mocked if it matches the given string/RegExp/predicate. Can be chained. (i.e. "don't mock 'fetch' if the next request is for the given URL, otherwise use the default behavior")
  • fetch.isMocking(input, init):boolean - test utility function to see if the given url/request would be mocked. This is not a read only operation and any "MockOnce" will evaluate (and return to the default behavior)

For convenience, all the conditional mocking functions also accept optional parameters after the 1st parameter that call mockResponse or mockResponseOnce respectively. This allows you to conditionally mock a response in a single call.

Conditional Mocking examples

describe('conditional mocking', () => {
  const realResponse = 'REAL FETCH RESPONSE'
  const mockedDefaultResponse = 'MOCKED DEFAULT RESPONSE'
  const testUrl = defaultRequestUri
  let crossFetchSpy
  beforeEach(() => {
    fetch.resetMocks()
    fetch.mockResponse(mockedDefaultResponse)
    crossFetchSpy = jest
      .spyOn(require('cross-fetch'), 'fetch')
      .mockImplementation(async () =>
        Promise.resolve(new Response(realResponse))
      )
  })

  afterEach(() => {
    crossFetchSpy.mockRestore()
  })

  const expectMocked = async (uri, response = mockedDefaultResponse) => {
    return expect(request(uri)).resolves.toEqual(response)
  }
  const expectUnmocked = async uri => {
    return expect(request(uri)).resolves.toEqual(realResponse)
  }

  describe('once', () => {
    it('default', async () => {
      const otherResponse = 'other response'
      fetch.once(otherResponse)
      await expectMocked(defaultRequestUri, otherResponse)
      await expectMocked()
    })
    it('dont mock once then mock twice', async () => {
      const otherResponse = 'other response'
      fetch
        .dontMockOnce()
        .once(otherResponse)
        .once(otherResponse)

      await expectUnmocked()
      await expectMocked(defaultRequestUri, otherResponse)
      await expectMocked()
    })
  })

  describe('doMockIf', () => {
    it("doesn't mock normally", async () => {
      fetch.doMockIf('http://foo')
      await expectUnmocked()
      await expectUnmocked()
    })
    it('mocks when matches string', async () => {
      fetch.doMockIf(testUrl)
      await expectMocked()
      await expectMocked()
    })
    it('mocks when matches regex', async () => {
      fetch.doMockIf(new RegExp(testUrl))
      await expectMocked()
      await expectMocked()
    })
    it('mocks when matches predicate', async () => {
      fetch.doMockIf(input => input.url === testUrl)
      await expectMocked()
      await expectMocked()
    })
  })

  describe('dontMockIf', () => {
    it('mocks normally', async () => {
      fetch.dontMockIf('http://foo')
      await expectMocked()
      await expectMocked()
    })
    it('doesnt mock when matches string', async () => {
      fetch.dontMockIf(testUrl)
      await expectUnmocked()
      await expectUnmocked()
    })
    it('doesnt mock when matches regex', async () => {
      fetch.dontMockIf(new RegExp(testUrl))
      await expectUnmocked()
      await expectUnmocked()
    })
    it('doesnt mock when matches predicate', async () => {
      fetch.dontMockIf(input => input.url === testUrl)
      await expectUnmocked()
      await expectUnmocked()
    })
  })

  describe('doMockOnceIf (default mocked)', () => {
    it("doesn't mock normally", async () => {
      fetch.doMockOnceIf('http://foo')
      await expectUnmocked()
      await expectMocked()
    })
    it('mocks when matches string', async () => {
      fetch.doMockOnceIf(testUrl)
      await expectMocked()
      await expectMocked()
    })
    it('mocks when matches regex', async () => {
      fetch.doMockOnceIf(new RegExp(testUrl))
      await expectMocked()
      await expectMocked()
    })
    it('mocks when matches predicate', async () => {
      fetch.doMockOnceIf(input => input.url === testUrl)
      await expectMocked()
      await expectMocked()
    })
  })

  describe('dontMockOnceIf (default mocked)', () => {
    it('mocks normally', async () => {
      fetch.dontMockOnceIf('http://foo')
      await expectMocked()
      await expectMocked()
    })
    it('doesnt mock when matches string', async () => {
      fetch.dontMockOnceIf(testUrl)
      await expectUnmocked()
      await expectMocked()
    })
    it('doesnt mock when matches regex', async () => {
      fetch.dontMockOnceIf(new RegExp(testUrl))
      await expectUnmocked()
      await expectMocked()
    })
    it('doesnt mock when matches predicate', async () => {
      fetch.dontMockOnceIf(input => input.url === testUrl)
      await expectUnmocked()
      await expectMocked()
    })
  })

  describe('doMockOnceIf (default unmocked)', () => {
    beforeEach(() => {
      fetch.dontMock()
    })
    it("doesn't mock normally", async () => {
      fetch.doMockOnceIf('http://foo')
      await expectUnmocked()
      await expectUnmocked()
    })
    it('mocks when matches string', async () => {
      fetch.doMockOnceIf(testUrl)
      await expectMocked()
      await expectUnmocked()
    })
    it('mocks when matches regex', async () => {
      fetch.doMockOnceIf(new RegExp(testUrl))
      await expectMocked()
      await expectUnmocked()
    })
    it('mocks when matches predicate', async () => {
      fetch.doMockOnceIf(input => input.url === testUrl)
      await expectMocked()
      await expectUnmocked()
    })
  })

  describe('dontMockOnceIf (default unmocked)', () => {
    beforeEach(() => {
      fetch.dontMock()
    })
    it('mocks normally', async () => {
      fetch.dontMockOnceIf('http://foo')
      await expectMocked()
      await expectUnmocked()
    })
    it('doesnt mock when matches string', async () => {
      fetch.dontMockOnceIf(testUrl)
      await expectUnmocked()
      await expectUnmocked()
    })
    it('doesnt mock when matches regex', async () => {
      fetch.dontMockOnceIf(new RegExp(testUrl))
      await expectUnmocked()
      await expectUnmocked()
    })
    it('doesnt mock when matches predicate', async () => {
      fetch.dontMockOnceIf(input => input.url === testUrl)
      await expectUnmocked()
      await expectUnmocked()
    })
  })

  describe('dont/do mock', () => {
    test('dontMock', async () => {
      fetch.dontMock()
      await expectUnmocked()
      await expectUnmocked()
    })
    test('dontMockOnce', async () => {
      fetch.dontMockOnce()
      await expectUnmocked()
      await expectMocked()
    })
    test('doMock', async () => {
      fetch.dontMock()
      fetch.doMock()
      await expectMocked()
      await expectMocked()
    })
    test('doMockOnce', async () => {
      fetch.dontMock()
      fetch.doMockOnce()
      await expectMocked()
      await expectUnmocked()
    })
  })
const expectMocked = async (uri, response = mockedDefaultResponse) => {
  return expect(request(uri)).resolves.toEqual(response)
}
const expectUnmocked = async uri => {
  return expect(request(uri)).resolves.toEqual(realResponse)
}

describe('conditional mocking complex', () => {
  const realResponse = 'REAL FETCH RESPONSE'
  const mockedDefaultResponse = 'MOCKED DEFAULT RESPONSE'
  const testUrl = defaultRequestUri
  let crossFetchSpy
  beforeEach(() => {
    fetch.resetMocks()
    fetch.mockResponse(mockedDefaultResponse)
    crossFetchSpy = jest
      .spyOn(require('cross-fetch'), 'fetch')
      .mockImplementation(async () =>
        Promise.resolve(new Response(realResponse))
      )
  })

  afterEach(() => {
    crossFetchSpy.mockRestore()
  })

  describe('complex example', () => {
    const alternativeUrl = 'http://bar'
    const alternativeBody = 'ALTERNATIVE RESPONSE'
    beforeEach(() => {
      fetch
        // .mockResponse(mockedDefaultResponse) // set above - here for clarity
        .mockResponseOnce('1') // 1
        .mockResponseOnce('2') // 2
        .mockResponseOnce(async uri =>
          uri === alternativeUrl ? alternativeBody : '3'
        ) // 3
        .mockResponseOnce('4') // 4
        .mockResponseOnce('5') // 5
        .mockResponseOnce(async uri =>
          uri === alternativeUrl ? alternativeBody : mockedDefaultResponse
        ) // 6
    })

    describe('default (`doMock`)', () => {
      beforeEach(() => {
        fetch
          // .doMock()    // the default - here for clarify
          .dontMockOnceIf(alternativeUrl)
          .doMockOnceIf(alternativeUrl)
          .doMockOnce()
          .dontMockOnce()
      })

      test('defaultRequestUri', async () => {
        await expectMocked(defaultRequestUri, '1') // 1
        await expectUnmocked(defaultRequestUri) // 2
        await expectMocked(defaultRequestUri, '3') // 3
        await expectUnmocked(defaultRequestUri) // 4
        // after .once('..')
        await expectMocked(defaultRequestUri, '5') // 5
        await expectMocked(defaultRequestUri, mockedDefaultResponse) // 6
        // default 'isMocked' (not 'Once')
        await expectMocked(defaultRequestUri, mockedDefaultResponse) // 7
      })

      test('alternativeUrl', async () => {
        await expectUnmocked(alternativeUrl) // 1
        await expectMocked(alternativeUrl, '2') // 2
        await expectMocked(alternativeUrl, alternativeBody) // 3
        await expectUnmocked(alternativeUrl) // 4
        // after .once('..')
        await expectMocked(alternativeUrl, '5') // 5
        await expectMocked(alternativeUrl, alternativeBody) // 6
        // default 'isMocked' (not 'Once')
        await expectMocked(alternativeUrl, mockedDefaultResponse) // 7
      })
    })

    describe('dontMock', () => {
      beforeEach(() => {
        fetch
          .dontMock()
          .dontMockOnceIf(alternativeUrl)
          .doMockOnceIf(alternativeUrl)
          .doMockOnce()
          .dontMockOnce()
      })

      test('defaultRequestUri', async () => {
        await expectMocked(defaultRequestUri, '1') // 1
        await expectUnmocked(defaultRequestUri) // 2
        await expectMocked(defaultRequestUri, '3') // 3
        await expectUnmocked(defaultRequestUri) // 4
        // after .once('..')
        await expectUnmocked(defaultRequestUri) // 5
        await expectUnmocked(defaultRequestUri) // 6
        // default 'isMocked' (not 'Once')
        await expectUnmocked(defaultRequestUri) // 7
      })

      test('alternativeUrl', async () => {
        await expectUnmocked(alternativeUrl) // 1
        await expectMocked(alternativeUrl, '2') // 2
        await expectMocked(alternativeUrl, alternativeBody) // 3
        await expectUnmocked(alternativeUrl) // 4
        // after .once('..')
        await expectUnmocked(alternativeUrl) // 5
        await expectUnmocked(alternativeUrl) // 6
        // default 'isMocked' (not 'Once')
        await expectUnmocked(alternativeUrl) // 7
      })
    })

    describe('doMockIf(alternativeUrl)', () => {
      beforeEach(() => {
        fetch
          .doMockIf(alternativeUrl)
          .dontMockOnceIf(alternativeUrl)
          .doMockOnceIf(alternativeUrl)
          .doMockOnce()
          .dontMockOnce()
      })

      test('defaultRequestUri', async () => {
        await expectMocked(defaultRequestUri, '1') // 1
        await expectUnmocked(defaultRequestUri) // 2
        await expectMocked(defaultRequestUri, '3') // 3
        await expectUnmocked(defaultRequestUri) // 4
        // after .once('..')
        await expectUnmocked(defaultRequestUri) // 5
        await expectUnmocked(defaultRequestUri) // 6
        // default 'isMocked' (not 'Once')
        await expectUnmocked(defaultRequestUri) // 7
      })

      test('alternativeUrl', async () => {
        await expectUnmocked(alternativeUrl) // 1
        await expectMocked(alternativeUrl, '2') // 2
        await expectMocked(alternativeUrl, alternativeBody) // 3
        await expectUnmocked(alternativeUrl) // 4
        // after .once('..')
        await expectMocked(alternativeUrl, '5') // 5
        await expectMocked(alternativeUrl, alternativeBody) // 6
        // default 'isMocked' (not 'Once')
        await expectMocked(alternativeUrl, mockedDefaultResponse) // 7
      })
    })

    describe('dontMockIf(alternativeUrl)', () => {
      beforeEach(() => {
        fetch
          .dontMockIf(alternativeUrl)
          .dontMockOnceIf(alternativeUrl)
          .doMockOnceIf(alternativeUrl)
          .doMockOnce()
          .dontMockOnce()
      })

      test('defaultRequestUri', async () => {
        await expectMocked(defaultRequestUri, '1') // 1
        await expectUnmocked(defaultRequestUri) // 2
        await expectMocked(defaultRequestUri, '3') // 3
        await expectUnmocked(defaultRequestUri) // 4
        // after .once('..')
        await expectMocked(defaultRequestUri, '5') // 5
        await expectMocked(defaultRequestUri, mockedDefaultResponse) // 6
        // default 'isMocked' (not 'Once')
        await expectMocked(defaultRequestUri, mockedDefaultResponse) // 7
      })

      test('alternativeUrl', async () => {
        await expectUnmocked(alternativeUrl) // 1
        await expectMocked(alternativeUrl, '2') // 2
        await expectMocked(alternativeUrl, alternativeBody) // 3
        await expectUnmocked(alternativeUrl) // 4
        // after .once('..')
        await expectUnmocked(alternativeUrl) // 5
        await expectUnmocked(alternativeUrl) // 6
        // default 'isMocked' (not 'Once')
        await expectUnmocked(alternativeUrl) // 7
      })
    })
  })
})

jest-fetch-mock's People

Contributors

allenfang avatar bjacquet avatar daniellenguyen avatar davidjb avatar dnorth avatar doug-wade avatar dsebastien avatar ebutleriv avatar fleck avatar forivall avatar grahamthecoder avatar guria avatar hally9k avatar hscgavin avatar huanzhang89 avatar janhartmann avatar jasonberry avatar jefflau avatar junkboy0315 avatar keeganwatkins avatar lukyth avatar mctaggaj avatar nomangul avatar phra avatar rnicholus avatar simon-taft-wd avatar spirosikmd avatar treora avatar yinzara avatar yulianovdey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

jest-fetch-mock's Issues

response lacks blob()

When using jest-fetch-mock with Jest 22:
fetch.mockResponseOnce(new Blob()); returns a response, but response.blob() fails with TypeError: response.blob is not a function. Setting appropriate headers seems to have no effect. Is this an issue with jest-fetch-mock or my own tests?
Thanks!

Error in action creator Testing

Can anyone help me out about the error I am getting while testing "self.body is not a function"

This is my code

`it('creates GET_PROPERTY_DETAILS_REQUEST when getting details', () => {

    let normalizedData = normalize(propertyDetailsMockData, propertyDetails)
    fetch.mockResponse(propertyDetailsMockData)
    const initialState = {}
    // console.log(propertyDetails)
    //fetch.mockResponse(normalizedData)
    const store = mockStore({propertyDetails:initialState})

    console.log(store.getActions())
    const expectedActions = [
        { type: GET_PROPERTY_DETAILS_REQUEST, propertyId:"1853633" },
        { type: GET_PROPERTY_DETAILS_SUCCESS,  propertyId:"1853633", body:{propertyDetailsMockData}}
    ]


    return store.dispatch(getPropertyDetails('1853633')).then(() => {
        // return of async actions
        expect(store.getActions()).toEqual(expectedActions)
    })
})`

and test case fails with the error
"error": [TypeError: self.body.on is not a function],

Specific URL support?

Forgive me if I missed something in the manual, but it looks like the only way to mock multiple calls is via an array of mockResponses where you know the order. Or just knowing there is only one request. I have simple mock like this:

const data = [{
  "a": 1
}, {
  "b": 2
}];

fetch.mockResponse(JSON.stringify({data}));

When I had used jasmine-ajax in the past, I could ensure this was the response for a given request URL. Consider the case where I am mocking two calls that just need to return a 200 code. By looking at the test, I would have no idea what calls are being mocked other than going into the real object and checking the order of them.

ReferenceError: Response is not defined

Hi!

I'm experiencing a strange bug today. I was using your module successfully until this morning when suddenly all my test suites failed with error:

Test suite failed to run

    ReferenceError: Response is not defined

      at Object.<anonymous> (node_modules/jest-fetch-mock/src/index.js:11:24)
      at Object.<anonymous> (__tests__/setupJest.js:1:103)

The only thing I did was removing my node-modules folder and then npm install them again. I imagine that I had some modules (or version of modules) in cache that disappeared with the rm command and were replaced by a new one after that. 🤔
But it doesn't seems that any module associated with jest-fetch-mock has changed since yesterday.

I've attached a little project that should reproduce the bug. Just unzip it and run:

npm install
npm test

Thanks in advance for your help!

BugFetchJest.zip

allow to inject argument for Promise.reject() into mockReject and mockRejectOnce

While using mockReject() or mockRejectOnce() it could be practical to allow to define the reason of the rejection. Currently both methods just do a Promise.reject() without any argument.

Scenario:

function collect (data, callback) {
  fetch('someUrl', init)
   .catch((error) => {
     callback(error)
   })
}

const callback = (error, response) => {
  //do something on error
}
collect({some: 'data'}, callback)

What I think would be nice to have:

test('test if callback is getting called with error argument', (done) => {
  fetch.mockRejectOnce(new Error('boom'))
  const callback = function (error) {
    expect(error).toEqual(expect.any(Error))
    done()
  }
 collect({}, callback)
})

If this is something you would support I will create a PR.

Unexpected behaviour while mocking fetch failure case

Hi, my successful mock call is working fine but I am getting this error in case of failure case:

  >>> ACTIONS -- User Actions   Asynchronous Action Creators  User Login Action Creator -- Failure

    Failed: Failed!

      at stackFormatter (node_modules/jest/node_modules/jest-cli/node_modules/jest-config/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)
      at process._tickCallback (internal/process/next_tick.js:109:7)

Here the my test code:

    test('User Login Action Creator -- Failure', () => {
      const expectedActions = [
        actions.fetchUser(),
        actions.unauthUser(),
        actions.fetchUserFailure()
      ]
      const store = mockStore({
        user : {}
      })

      fetch.mockReject(new Error('Failed!')) // 

      return store
        .dispatch(actions.userLogin({
          userName : '',
          password : ''
        }))
        .then(() => {
          expect(store.getActions()).toEqual(expectedActions)
        })
    })

I am not sure what am I missing here.

Typescript types do not include returned values, important object attributes

mockResponse, mockResponseOnce, etc. do not return void

According to index.d.ts, mockResponse, etc all return void.

mockResponse(body: string, init?: MockParams): void;
mockResponseOnce(body: string, init?: MockParams): void;
mockResponses(responses: Array<{body: string, init?: MockParams}>): void;

In actuality, they all return the mocked fetch fn., so we can chain mockResponse calls.

fetch.mock isn't included in the definition

Since mock isn't included in the definition and the fetch definition doesn't extend jest's mocked function definition, uses of .mock.calls fail type checking.

Unexpected end of JSON input with CRA

I am using this with Create-React-App and react-app-rewired but i am getting error on running tests:

...node_modules/react-scripts/scripts/test.js:20
  throw err;
  ^

SyntaxError: Unexpected end of JSON input
    at parse (<anonymous>)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:182:7)
npm ERR! Test failed.  See above for more details.

Missing License

I was looking into using this library for one of our applications, but couldn't find a license explicitly specified. Now IANAL, but it seems like a legal hazard to use this library productively.

@jefflau, would you mind adding a license to the repo, so that we can use the code for testing our applications?

I would appreciate it a lot!

jest-fetch-mock being over ridden by actual fetch package

Hi there, I am having an issue with implementing the library, and wondering what the best way to tackle the issue is.

So in my redux project, we have an api file that makes service calls to our backend service, using isomorphic-fetch. This file imports the module and then invokes fetch on the given service urls.

I believe I have my test file setup correctly, the fetch object is the jest-fetch-mock object (has access to the mock methods, etc.) when the test starts up, but when the test passes through the api file it is using the fetch object from the isomorphic-fetch module instead of the mock module. Because I am importing isomorphic-fetch module directly into the file, it is over riding the global fetch object I set in the setupTests file. I have confirmed this by removing the import from the api file, and then the test will use the mock fetch object that was set as a global in the setup file.

So my question is what is wrong about my implementation? Any help would be appreciated.

ActualResponse is not a constructor

in my test

import React from 'react';
import { mount } from 'enzyme';
import Help from './Help';

describe('Help basic functionality', () => {
  it('should be able to show help', () => {
   fetch.mockResponse(JSON.stringify({access_token: '12345' }));
    fetch.mockResponseOnce(JSON.stringify({ name: 'naruto' }));
 
    const wrapper = mount(
      <Help content="transformation_test" />,
    );

    expect(wrapper.html()).toBe('<div class="help"><span>Increment</span></div>');
  });
});

but i getting

TypeError: ActualResponse is not a constructor
  
  at new ResponseWrapper (node_modules/jest-fetch-mock/src/index.js:33:10)
  at fetch.mockImplementationOnce (node_modules/jest-fetch-mock/src/index.js:48:27)
  at Help.loadContent (app/components/help/Help.jsx:46:14)

I have " global.fetch = require('jest-fetch-mock'); "

and in Help I just calling window.fetch

fetch.resetMocks(); makes the fetch call to return undefined.

Hi!
I have a problem using the fetch.resetMocks(); in the before each statement.

// app/utils/tests/request.test.js

import request from '../request';

describe('request', () => {
 beforeEach(() => {
   fetch.resetMocks();
 });
 it('perform the fetch with the default headers', () => {
   const defaultHeaders = {
     'Content-Type': 'application/json',
     'Access-Control-Allow-Origin': '*',
     'Access-Control-Allow-Headers': 'Content-Type',
   }
   request('http://fake_url', {})

   expect(fetch.mock.calls.length).toBe(1);
   expect(fetch.mock.calls[0][1].headers).toEqual(defaultHeaders);
 });

// app/utils/request.js
....
export default function request(
 url,
 extraHeaders = {},
 method = 'GET',
 body = undefined,
) {
 const headers = Object.assign(
   { headers: Object.assign(CORSHeaders.headers, extraHeaders) },
   { method },
   { body: JSON.stringify(body) },
 );

 return fetch(url, headers)
   .then(checkStatus)
   .then(parseJSON);
}

Executing the test with or without fetch.resetMocks(); affects the response of the test.

Without it works well.
With I get

 FAIL  app/utils/tests/request.test.js
  ● request › perform the fetch with the default headers

    TypeError: Cannot read property 'then' of undefined

      60 |   );
      61 | 
    > 62 |   return fetch(url, headers)
         |          ^
      63 |     .then(checkStatus)
      64 |     .then(parseJSON);
      65 | }

      at request (app/utils/request.js:62:10)
      at Object.<anonymous> (app/utils/tests/request.test.js:17:5)

I want to execute more than one test over fetch in this file so I really need to reset it.
Great project by the way.
Cheers.

Build fails on create-react-app

yarn build fails with the following output:

Failed to minify the code from this file:

        ./node_modules/jest-fetch-mock/src/index.js:11

Read more here: http://bit.ly/2tRViJ9

Mocking headers

Is there a good way to mock the headers that are returned with a response? I have a module that checks the content-type returned, but I'm having a hard time testing because I can't actually set the content-type header of the mocked response.

mockResponses return value is not well-documented

After using it I discovered it returns array of mock functions. However, it's actually the same mock function over and over. I think it would make more sense to just return a single function, but either way, the return value should be better documented.

[question] Assert after fetch

Hi, I'm having trouble finding a way to make assertions after the fetch resolves. My case is that I have a component which makes a fetch when a button is clicked, and I am using enzyme to simulate a click on that button. I want to make an assertion about the state of the component after the fetch resolves/rejects, but I can't figure out how to attach a then onto the mocked fetch itself. Any suggestions? Does that make sense?

Basically, I'm trying to do something like enzymejs/enzyme#346 (comment) with this package instead of sinon.

FormData is not defined

Can't use the lib for testing FormData bodies it seems - getting FormData is not defined.

Can no longer flush promises since 1.6.0

I have some tests where I want to await the outcome of the results of fetch requests. The tests look like this

const manipulateDom = async () => {
    const wrapper = mount(<Provider store={store}><MyContainer /></Provider>);
    wrapper.find('button').simulate('click');
    await () => new Promise(resolve => setImmediate(resolve));
    return wrapper;
};
it('handles failures', async () => {
        fetch.mockReject(new Error('The request failed!'));

        const wrapper = await manipulateDom();

        expect(wrapper.find('results').html()).toContain('Failed to send contacts with error: {0}');
});

Note that I'm testing logic that does not return the promise, so I can't await it directly. Instead, I rely on the fact that the mock will resolve on next tick, and await setImmediate to wait for the next tick before making my assertions. This worked great, but it seems that the new Promise polyfill introduced in this PR has broken this behavior (afaict, since it also uses setImmediate as well, we can no longer be certain that has been resolved before our setImmediate. I'm a little hazy on the detail, though, as I haven't spelunked very far into the implementations of the promise polyfill).

mockResponse returning undefined

I'm sure I'm missing something pertaining to using this lib, but fetch.mockResponse is returning 'undefined' for me.

I've assigned fetch to your library in my jestsetup.js per the instructions. In my test I've just written:

const source = fetch.mockResponse(JSON.stringify({test: '123'}))
console.log(source)

source is undefined. What'd I miss?

Thanks

Inspecting fetch URL and options

When testing it would sometimes be useful to inspect the fetch URL and options being passed: checking for the correct headers, POST body content, etc. Is there any way to achieve this using jest-fetch-mock?

mockResponse fail case

Is there a mockResponse fail case scenario, in the code I dont see a Promise that rejects?

Problem with typescript import

As a part of my build routine I call tsc --noEmit.

Earlier this printed nothing to my command-line.

I added jest-fetch-mock to my project, used a setupFile with global.fetch = require('jest-fetch-mock') as recommended.

If I dont add an import statement for the jest-fetch-mock in the typescript source test files where the jest-fetch-mock of fetch is used, then:

  • jest runs the tests fine
  • tsc complains that i.e. Property 'resetMocks' does not exist on type '(input?: string | Request, init?: RequestInit) => Promise<Response>'.

If I do add an import statement for the jest-fetch-mock in the typescript source test files where the jest-fetch-mock of fetch is used, then:

  • jest runs the tests fine
  • tsc complaints: node_modules/@types/jest/index.d.ts:34:15 - error TS2451: Cannot redeclare block-scoped variable 'expect'. plus many error messages after this (which I guess is caused for the same reason).

The only work-around I know for this is to add a const fetchAny = fetch as any; at the top of the source test-files where I want to use jest-fetch-mock, and use that object instead to do the testing.
Which is not ideal. The whole purpose of the typescript types is to be able to benefit from the typings. Casting to any means that it becomes an ordinary object.

Has anyone encountered this and found any better solutions?

support for node-fetch ?

Hi, I couldn't make this package work on nodejs to mock 'node-fetch' package.
Could you give me some directions so that I can contribute to make this package work for nodejs too ?
Thanks!

[updates]
I found this configuration works with nodejs
package.json

"jset": {
    "testEnvironment": "node",
//setupJest.js
import fetch from 'jest-fetch-mock'
jest.setMock('node-fetch', fetch)

The problem now is that Request and Response are not present in nodejs
I think it could be cool if this package could read jest configuration,
then if testEnvironment == node , use Request and Response polyfills/mocks

Promise suggestion

Hey @jefflau

Preface

About Promise.finally is already going to stage 4, some promise library already support finally function.

Issue

Recently, I used finally with fetch in my code, everything is work well, but when I writing the test leverage by jest-fetch-mock, there's something wrong. Anyway, the modern browser's fetch already support finally except for Edge. you can try it easily on browser dev tool

Root cause

I think due to jest-fetch-mock still use native Promise so that some people will got above issue.

Do you consider to use promise-polyfill to fix this issue?, it's already support finally

Probably, I can try to fix this issue by a PR after your confirmation

TypeError: fetch.resetMocks is not a function

I'm using react-scripts 1.1.4, even tried updating to 2.0.0-next.3e165448, same deal.

Here's the code I'm using:

// setupTests.js
import fetchMock from 'jest-fetch-mock'
global.fetch = fetchMock
// test.js
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'

const mockStore = configureMockStore([thunk])

import * as C from './types'
import * as actions from './actions'
import reducer from './reducer'

describe('mds actions', () => {
  beforeEach(() => {
    console.log(fetch)
    fetch.resetMocks()
  })

  it('should create a MDS_SET_SCORES', () => {
    expect(actions.setScores()).toEqual({
      type: C.MDS_SET_SCORES
    })
  })

I get the following errors:

  ● mds actions › should create a MDS_SET_SUMMARY

    TypeError: fetch.resetMocks is not a function

The log results are:

{ [Function: mockConstructor]
        _isMockFunction: true,
        getMockImplementation: [Function],
        mock: [Getter/Setter],
        mockClear: [Function],
        mockReset: [Function],
        mockReturnValueOnce: [Function],
        mockResolvedValueOnce: [Function],
        mockRejectedValueOnce: [Function],
        mockReturnValue: [Function],
        mockResolvedValue: [Function],
        mockRejectedValue: [Function],
        mockImplementationOnce: [Function],
        mockImplementation: [Function],
        mockReturnThis: [Function],
        mockName: [Function],
        getMockName: [Function],
        mockRestore: [Function],
        Headers: [Function: Headers],
        Response: [Function: ResponseWrapper],
        Request: [Function: Request],
        mockResponse: [Function],
        mockReject: [Function],
        mockResponseOnce: [Function: mockResponseOnce],
        once: [Function: mockResponseOnce],
        mockRejectOnce: [Function],
        mockResponses: [Function],
        resetMocks: [Function] }

Any help here is appreciated. Really like the design of this module but it does not seem to be working with create react app.

Response, Headers, Request undefined

As mentioned in comment on 84c4f69 , I get undefined for these values on master branch - but if I use

const { Response, Headers, Request } = require('whatwg-fetch');

instead of

require('whatwg-fetch');

it works.

Network Request Failing on Test

I'm getting a really weird issue where my network request is failing in the test with jest-fetch-mock, despite working when I run the app locally.

My test looks like this

// Syntactic sugar, see:
// https://github.com/facebook/jest/issues/2157#issuecomment-279171856 something
// like this will maybe added to the Jest API
function flushPromises() {
    return new Promise(resolve => setImmediate(resolve));
}

const muiTheme = getMuiTheme();

it('renders without crashing', () => {
    jestMockFetch.mockResponse(JSON.stringify({meta: {status: "success"}});

    const component = mount(
        <VerifyModal/>, {
        context: {
            muiTheme
        },
        childContextTypes: {
            muiTheme: PropTypes.object
        }
    });

    return flushPromises().then(() => {
        expect(component.find('h1')).toHaveLength(1);
        expect(jestMockFetch.mock.calls).toHaveLength(1);
    });
});

my setupTests.js

global.jestMockFetch = require('jest-fetch-mock');

And then this is the result of the test

 FAIL  src\views\VerifyModal.test.js
  ● renders without crashing

    expect(received).toHaveLength(length)

    Expected value to have length:
      1
    Received:
      {"length": 0, Symbol(enzyme.__unrendered__): null, Symbol(enzyme.__rendere
r__): {"batchedUpdates": [Function batchedUpdates], "getNode": [Function getNode
], "render": [Function render], "simulateEvent": [Function simulateEvent], "unmo
unt": [Function unmount]}, Symbol(enzyme.__root__): {"length": 1, Symbol(enzyme.
__unrendered__): <VerifyModal />, Symbol(enzyme.__renderer__): [Object], Symbol(
enzyme.__root__): [Circular], Symbol(enzyme.__node__): [Object], Symbol(enzyme._
_nodes__): [Array], Symbol(enzyme.__options__): [Object]}, Symbol(enzyme.__node_
_): undefined, Symbol(enzyme.__nodes__): Array [], Symbol(enzyme.__options__): {
"adapter": [Object], "childContextTypes": [Object], "context": [Object]}}
    received.length:
      0

      at flushPromises.then (src/views/VerifyModal.test.js:43:56)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:160:7)

  × renders without crashing (127ms)
  √ shows loading screen if formView is "loading"
  √ shows dob-form screen if formView is "dob-form"
  √ shows upload-form screen if formView is "upload-form"
  √ shows upload-success screen if formView is "upload-success"
  √ shows error-message screen if formView is "error-message"
  √ shows rejected-verification screen if formView is "rejected-verification"

Test Suites: 1 failed, 1 total
Tests:       1 failed, 6 passed, 7 total
Snapshots:   0 total
Time:        1.819s
Ran all test suites related to changed files.

  console.error node_modules\raven-js\src\console.js:32
    There was an error getting the settings: TypeError: Network request failed

As you can see at the bottom, my network request failed.

SyntaxError: Unexpected token [

● Test suite failed to run

SyntaxError: Unexpected token [

  at transformAndBuildScript (../node_modules/jest-runtime/build/transform.js:284:10)
  at Object.<anonymous> (setupTests.js:1:809)

package.json

{
  "name": "super-admin-skeleton",
  "version": "1.0.0",
  "description": "ECP skeleton for React/Redux super admin application",
  "scripts": {
    "build": "NODE_ENV=production ./node_modules/webpack/bin/webpack.js --config ./webpack.prod.config.js --progress --profile --colors",
    "prod": "node prod.js",
    "snapshots": "jest -u",
    "start": "node server.js",
    "test": "jest"
  },
  "author": "ECP",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/EastCoastProduct/super-admin-skeleton"
  },
  "devDependencies": {
    "babel-core": "^6.16.0",
    "babel-eslint": "^7.0.0",
    "babel-jest": "^16.0.0",
    "babel-loader": "^6.2.5",
    "babel-preset-es2015": "^6.16.0",
    "babel-preset-react": "^6.16.0",
    "babel-preset-react-hmre": "^1.1.1",
    "babel-preset-stage-0": "^6.16.0",
    "connect-history-api-fallback": "^1.3.0",
    "css-loader": "^0.25.0",
    "enzyme": "^2.4.1",
    "eslint": "^3.6.1",
    "eslint-config-airbnb": "^12.0.0",
    "eslint-loader": "^1.5.0",
    "eslint-plugin-import": "^2.0.0",
    "eslint-plugin-jsx-a11y": "^2.2.2",
    "eslint-plugin-react": "^6.3.0",
    "express": "^4.14.0",
    "file-loader": "^0.9.0",
    "html-webpack-plugin": "^2.22.0",
    "jest": "^16.0.0",
    "jest-fetch-mock": "^1.0.6",
    "react-addons-test-utils": "^15.3.2",
    "react-test-renderer": "^15.3.2",
    "redux-mock-store": "^1.2.1",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "webpack": "^1.13.2",
    "webpack-dev-middleware": "^1.8.3",
    "webpack-hot-middleware": "^2.12.2"
  },
  "dependencies": {
    "babel-polyfill": "^6.16.0",
    "es6-promise": "^4.0.3",
    "font-awesome": "^4.6.3",
    "immutable": "^3.8.1",
    "react": "^15.3.2",
    "react-dom": "^15.3.2",
    "react-redux": "^4.4.5",
    "react-router": "^2.8.1",
    "recompose": "^0.20.2",
    "redux": "^3.6.0",
    "redux-form": "^6.0.5",
    "redux-immutablejs": "0.0.8",
    "redux-thunk": "^2.1.0",
    "store": "^1.3.20",
    "validator": "^6.0.0",
    "whatwg-fetch": "^1.0.0"
  },
  "jest": {
    "cacheDirectory": "../test/cache",
    "collectCoverage": true,
    "collectCoverageFrom": [
      "**/*.js"
    ],
    "coverageDirectory": "../test/coverage",
    "globals": {
      "__APP_URL__": "http://192.168.50.4:9000",
      "__API_URL__": "http://192.168.50.4:3000"
    },
    "setupFiles": [
      "./setupTests.js"
    ],
    "testEnvironment": "node",
    "timers": "fake",
    "rootDir": "src"
  }
}

setupTest.js

import jestMockFetch from 'jest-fetch-mock';

global.fetch = jestMockFetch;

EDIT: Removing testEnvironment and using jsdom doesn't help. I see I can't use your module as I'm running my tests in node environment because it's much faster than jsdom so I'm going to mock it by myself.

First example does not work as expected

I'm talking about the "simple mock and assert" example. I copied the api.js and api.test.js exactly. I'll paste them here for reference

//api.js
export function APIRequest(who) {
  if (who === 'google') {
    return fetch('https://google.com').then(res => res.json())
  } else {
    return 'no argument provided'
  }
}
//api.test.js
import { APIRequest } from './api'

describe('testing api', () => {
  beforeEach(() => {
    fetch.resetMocks()
  })

  it('calls google and returns data to me', () => {
    fetch.mockResponseOnce(JSON.stringify({ data: '12345' }))

    //assert on the response
    APIRequest('google').then(res => {
      expect(res.data).toEqual('12345')
    })

    //assert on the times called and arguments given to fetch
    expect(fetch.mock.calls.length).toEqual(1)
    expect(fetch.mock.calls[0][0]).toEqual('https://google.com')
  })
})

I tried changing each "expect" to verify the test fails. The last two work perfectly, it verified that https://google.com was called exactly once.

The first expect (inside the APIRequest) does not fail when I change it to 'abcde'. I put a console log right above the expect to make sure it's actually being run, and it printed no problem. I put another console log underneath the expect, and that is NOT being printed.

        APIRequest('google').then(res => {
            console.log("Before: " + res.data); // prints "Before: 12345"
            expect(res.data).toEqual('abcde')
            console.log("After: " + res.data); // Does not print
        })

Why would this be happening? I'm running v1.6.2, Windows 10.

mockResponseOnce not isolating

Given the following test one would expect the logged result to have a length of to containing the action of RECEIVE_SCHEDULE_IMAGE_PENDING and RECEIVE_SCHEDULE_IMAGE_FAILED yet i get a response length of 4 containing the response of the previous test case.

import { fetchSchedules } from 'shared/freemium/actions';
import { mockStore } from 'tests/mocks';
import {
  RECEIVE_SCHEDULE_IMAGE_PENDING,
  RECEIVE_SCHEDULE_IMAGE_SUCCESS,
  RECEIVE_SCHEDULE_IMAGE_FAIL
} from 'shared/state/action-types';

const store = mockStore({
  shared: {
    freemium: {
      schedulePreviews: {
        loaded: false,
      },
    },
  },
});

it('tries to fetch schedules', () => {
  fetch.mockResponseOnce('{}', { status: 200 });

  return store.dispatch(fetchSchedules())
    .then(() => {
      const expectedActions = store.getActions();
      expect(expectedActions.length).toBe(2);
      expect(expectedActions[0]).toEqual({ type: RECEIVE_SCHEDULE_IMAGE_PENDING });
      expect(expectedActions[1]).toEqual({ type: RECEIVE_SCHEDULE_IMAGE_SUCCESS, payload: {} });
    });
});

it('will fail with bad status code', () => {
  fetch.mockResponseOnce('{"test": 1}');

  return store.dispatch(fetchSchedules())
    .then(() => {
      const expectedActions = store.getActions();
      console.log(expectedActions);
      expect(expectedActions.length).toBe(2);
      expect(expectedActions[0]).toEqual({ type: RECEIVE_SCHEDULE_IMAGE_PENDING });
      expect(expectedActions[1]).toEqual({ type: RECEIVE_SCHEDULE_IMAGE_SUCCESS, payload: {} });
    });
});

from the following action creator

import {
  RECEIVE_SCHEDULE_IMAGE_SUCCESS,
  RECEIVE_SCHEDULE_IMAGE_PENDING,
  RECEIVE_SCHEDULE_IMAGE_FAIL,
} from 'shared/state/action-types';
import { request } from 'shared/auth';

function shouldFetchSchedules(state) {
  return !state.shared.freemium.schedulePreviews.loaded;
}

function fetchSchedulesSuccess(json) {
  return {
    type: RECEIVE_SCHEDULE_IMAGE_SUCCESS,
    payload: json,
  };
}

function fetchSchedulesPending() {
  return {
    type: RECEIVE_SCHEDULE_IMAGE_PENDING,
  };
}

function fetchSchedulesFail(e) {
  return {
    type: RECEIVE_SCHEDULE_IMAGE_FAIL,
    error: e,
  };
}

export function fetchSchedules() {
  return (dispatch, getState) => {
    if (shouldFetchSchedules(getState())) {
      dispatch(fetchSchedulesPending());
      return request().get('/schedule/images')
        .then(json => dispatch(fetchSchedulesSuccess(json)))
        .catch(e => dispatch(fetchSchedulesFail(e)));
    }
  };
}

Is this a problem with generators?

First of all, congratulations for the nice lib!
Not sure if I am missing something out here, but this is what happened:
I tried to use the lib with generators and it didn't work. The yielded response is a strange object that do not contains the methods json and text, thus making impossible to proceed with the operation.
Is this issue known by any of the mainteiners?

fetch.resetMocks not resetting fetch

So as the title implies fetch.mockResets() isn't resetting the the fetch.

here is my full test.js:

import Worker from '../statistics-worker/worker';
jest.mock("../statistics-worker/worker");
const workerInitProps = {
  interactionId: '123',
  interactionSubmissionUrl: 'submission',
  fetchUrl: 'fetch'
};
const statService = Statistic.getNewInstance(new Worker(workerInitProps));
describe('Statistic serivce ', () => {
  beforeEach(() => {
    fetch.resetMocks();
  });
  it('Should init worker', () => {
    expect(statService).toBeInstanceOf(Statistic);
  });
  it('should fetch statistics', () => {
    fetch.mockResponseOnce(
      JSON.stringify({
        payload: {
          statistics: {
            1: 10,
            2: 11
          },
          count: 10,
          finalResults: []
        }
      })
    );
    statService
      .fetchStatistics()
      .then(res => {
        expect(res).toEqual({
          statistics: {
            1: {
              options: 10,
              totalVotes: 0
            },
            2: {
              options: 11,
              totalVotes: 0
            }
          },
          count: 10,
          finalResults: []
        });
      })
      .catch(err => {
        expect(err).toBeFalsy();
      });
  });

  it('should deadlock fetch statistics', () => {
    fetch.mockRejectOnce(new Error('fake error message'));

    const statistics = Statistic.getNewInstance(new Worker(workerInitProps));
    statService
      .fetchStatistics()
      .then(res => {
        console.log(res);
        expect(res).toEqual({
          count: 0,
          statistics: {},
          dead: true
        });
      })
      .catch(err => {
        expect(err).toBeFalsy();
      });
  });
});

The result i get is:

Expected value to equal:
  {"count": 0, "dead": true, "statistics": {}}
Received:
  {"count": 10, "finalResults": [], "statistics": {"1": {"options": 10, "totalVotes": 0}, "2": {"options": 11, "totalVotes": 0}}}

setupJest.js bug

Using create-react-app, the below didn't work for me

//setupJest.js or similar file 
global.fetch = require('jest-fetch-mock')

Changed to this:

import fetch from 'jest-fetch-mock'
global.fetch = fetch

Not sure if this just happens for me but may have to consider change in documentation.

Cheers

mock fetch response error

is there a way to mock fetch response errors?

e.g.

try {
	const response = await fetch(
		'https://jsonplaceholder.typicode.com/posts/1001',
	);
	if (!response.ok) {
		throw response;
	}
} catch (e) {
	const formatted = await formatFetchResponseError(e);
	expect(formatted).toMatchSnapshot();
}

"ReferenceError: self is not defined" in all test suites

Using Jest 15.1.1, I'm trying to add jest-fetch-mock to my project. I've added a setupJestFetchMock.js file with just global.fetch = require('jest-fetch-mock') in it, and referenced this file from my Jest configuration. I didn't set automock to false since that's the default since Jest 15.

All my test suites now fail with

  ● Test suite failed to run

    ReferenceError: self is not defined

      at Object.<anonymous> (node_modules/jest-fetch-mock/src/index.js:11:28)
      at Object.<anonymous> (setupJestFetchMock.js:1:683)

This seems a bit odd to me, the instructions for setting up weren't that hard and yet I manage to get some strange error. Did I make a mistake somewhere?

ES5 examples

I'd like to use this but I don't understand how it works from the readme. What's wrong with writing plain old, clear ES5 examples?

Removes the need for users to have to get their head around ES6 syntax before then trying to understand your lib.

Just my 2c ;)

next mockResponse read previous object

if I have

it('test on 404', () => {
fetch.mockResponse('Not found', { status: 404 });
}

it('test on change', () => {
fetch.mockResponse('another content');
}

on second mock i get also status 404... if i change it to
fetch.mockResponse(testContent, { status: 200 }); then it's working

fetchData function return value is not excepted but undefined

export function requestGet(url) {
    return fetch(url).then(
        response => {
            response.json();
        }
    ).then(
        json => {
            console.log("json is:" + json);
            return {
                state: "200",
                jsonStr: json
            }
        }).catch((error) => {
        console.log("error", error);
        return {
            state: "400",
            jsonStr: error
        }
    });
}

describe('test requestGet function()', () => {
    it('success',async () => {
        const response = '{"items": [{"id": 1}]}';
        fetch.mockResponse(response);
        url = "testurl";
        const result = await requestGet(url);
        console.log("result is:" + result.state + ">>>" + result.jsonStr);
    });
});

which print result is:200>>>undefined

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.