jefflau / jest-fetch-mock

Jest mock for fetch
MIT License
886 stars 118 forks source link

First example does not work as expected #69

Closed JacobFrericks closed 4 years ago

JacobFrericks commented 6 years ago

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.

jefflau commented 6 years ago

That's a bit confusing. Can you create a repo and I'll have a look?

JacobFrericks commented 6 years ago

Sorry for the delay, here is the repository: https://github.com/JacobFrericks/jest-temp/

Basically I used the "create-react-native-app" tool, installed the jest-fetch-mock module, then added the api.js and api.test.js from the examples and pasted them in. I changed one line in the test, expecting it to fail, but it still passes.

IanVS commented 6 years ago

I believe you need to use the done callback to let jest know when the test is finished, like so:

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

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

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

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

    //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')
  })
})
JacobFrericks commented 6 years ago

I tried that, and it does indeed do what I want! All the tests fail when they should, and pass when they should. However, there is one weird thing.

If I change the last two expects to fail, say by changing the last one to be

expect(fetch.mock.calls[0][0]).toEqual('https://thisShouldFail.com')

I get the expected error:

Error: expect(received).toEqual(expected)

Expected value to equal:
  "https://thisShouldFail.com"
Received:
  "https://google.com"

However, if I change the first expect to be this:

expect(res.data).toEqual('67890')

I get an unexpected error:

Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

I expected something like "Expected value to equal: 12345 Received: 67890". Why did I get this other error?

Thank you for helping with this!

JacobFrericks commented 6 years ago

I updated my repository with the issue. If you run the test, you get the Async error.

https://github.com/JacobFrericks/jest-temp/

JacobFrericks commented 6 years ago

@jefflau @IanVS does my issue make sense?

linconkusunoki commented 6 years ago

@JacobFrericks @jefflau @IanVS any update? I'm having the same problems

luissmg commented 6 years ago

Guys, @JacobFrericks @jefflau @IanVS @linconkusunoki I think we are doing this wrong. Let me explain to you. Why don't you use async instead of done?

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
    await APIRequest('google').then(res => {
      expect(res.data).toEqual('12345');
    });

    // You can do this too
    // 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');
  })
})

I was having the same problem has you guys and one more. In my function, in this case APIRequest, I needed to access to React Native AsyncStorage. To do this, I need to do it asynchronously. So I used async and await. It fixed my problem, BUT created another one. All the assertions after the call to the API function would not validate. I could put wrong values and it would still say they were valid.

The solution above solved everything for me, even the problem with the timeout:

Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

Give it a try and let me know if it worked for you!

luissmg commented 6 years ago

@jefflau Maybe we should update the docs with this?

tjmabey commented 6 years ago

I was having the same issue with the first example. I got it to work by adding return in front of the assert based on reading the Jest docs on promises.

Be sure to return the promise - if you omit this return statement, your test will complete before fetchData completes.

Docs should definitely be updated, because it can be very frustrating when one can't even get the most basic example to work correctly.

toklok commented 5 years ago

Glad I came across this, here was my solution

import { APIRequest } from './../src/api'

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

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

        const res = await APIRequest('google');
        //assert on the response
        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');
    })
})
gotofritz commented 4 years ago

Made a PR to add @toklok's fix to the README