jefflau / jest-fetch-mock

Jest mock for fetch
MIT License
882 stars 116 forks source link

Conditionally mocking an empty response body #189

Closed michielbdejong closed 3 years ago

michielbdejong commented 3 years ago

It seems that conditionally mocking an empty response body doesn't always work as expected:

import fetchMock from "jest-fetch-mock";

describe("Empty response bodies", () => {
  beforeEach(() => {
    fetchMock.enableMocks();
    fetchMock.resetMocks();
  });

  describe("Conditionally mock empty body", () => {
    beforeEach(() => {
      fetchMock.mockIf("https://example.com/", "");
    });
    it("Resolves to empty string", async () => {
      const result = await fetchMock("https://example.com/");
      const text = await result.text();
      expect(text).toEqual("");
    });
  });

  describe("Conditionally mock space body", () => {
    beforeEach(() => {
      fetchMock.mockIf("https://example.com/", " ");
    });
    it("Resolves to space string", async () => {
      const result = await fetchMock("https://example.com/");
      const text = await result.text();
      expect(text).toEqual(" ");
    });
  });
  describe("After mocking 404", () => {
    beforeEach(() => {
      fetchMock.mockResponse("Not Found", {
        status: 404,
      });
    });  
    describe("Conditionally mock empty body", () => {
      beforeEach(() => {
        fetchMock.mockIf("https://example.com/", "");
      });
      it("Resolves to empty string", async () => {
        const result = await fetchMock("https://example.com/");
        const text = await result.text();
        expect(text).toEqual("");
      });
    });

    describe("Conditionally mock space body", () => {
      beforeEach(() => {
        fetchMock.mockIf("https://example.com/", " ");
      });
      it("Resolves to space string", async () => {
        const result = await fetchMock("https://example.com/");
        const text = await result.text();
        expect(text).toEqual(" ");
      });
    });
  });
});

Expected output: all 4 tests pass Actual output:

$ ./node_modules/.bin/jest emptyResponseBodies.test.ts 
 FAIL  ./emptyResponseBodies.test.ts
  Empty response bodies
    Conditionally mock empty body
      ✓ Resolves to empty string (6 ms)
    Conditionally mock space body
      ✓ Resolves to space string (1 ms)
    After mocking 404
      Conditionally mock empty body
        ✕ Resolves to empty string (4 ms)
      Conditionally mock space body
        ✓ Resolves to space string (1 ms)

  ● Empty response bodies › After mocking 404 › Conditionally mock empty body › Resolves to empty string

    expect(received).toEqual(expected) // deep equality

    Expected: ""
    Received: "Not Found"

      41 |         const result = await fetchMock("https://example.com/");
      42 |         const text = await result.text();
    > 43 |         expect(text).toEqual("");
         |                      ^
      44 |       });
      45 |     });
      46 | 

      at _callee3$ (emptyResponseBodies.test.ts:43:22)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:63:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:293:22)
      at Generator.next (node_modules/regenerator-runtime/runtime.js:118:21)
      at asyncGeneratorStep (node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
      at _next (node_modules/@babel/runtime/helpers/asyncToGenerator.js:25:9)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 3 passed, 4 total
Snapshots:   0 total
Time:        2.49 s
Ran all test suites matching /emptyResponseBodies.test.ts/i.
yinzara commented 3 years ago

This is because we test for the presence of a response body with:

fetch.mockIf = fetch.doMockIf = (urlOrPredicate, bodyOrFunction, init) => {
  isMocking.mockImplementation(requestMatches(urlOrPredicate))
  if (bodyOrFunction) {
    fetch.mockResponse(bodyOrFunction, init)
  }
  return fetch
}

Which returns false for if (bodyOrFunction) in the case where you pass an empty string. I guess we could change them to checks for undefined but I really don't think your use case is something we really want to support.

michielbdejong commented 3 years ago

Fair enough! Too bad for us. :)

I'm trying to write a test where I want to check how my code reacts to an empty response body. If that's not supported by jest-fetch-mock then I'll find a different way to mock it.

Are you aware of any other fetch mock libraries we could try out instead for this? Maybe we should just mock fetch manually for this test?

yinzara commented 3 years ago

Oh sorry I assumed by posting the code you could see there is a work around but that's stupid to assume so here it is lol:

describe("Conditionally mock empty body", () => {
      beforeEach(() => {
        fetchMock.mockIf("https://example.com/");
        fetchMock.mockResponse("");
      });
      it("Resolves to empty string", async () => {
        const result = await fetchMock("https://example.com/");
        const text = await result.text();
        expect(text).toEqual("");
      });
    });