Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: How to define URL-specific mocks? #247

Open
haukehem opened this issue Jun 1, 2023 · 1 comment
Open

Question: How to define URL-specific mocks? #247

haukehem opened this issue Jun 1, 2023 · 1 comment

Comments

@haukehem
Copy link

haukehem commented Jun 1, 2023

Background:
I've created a React project with a Clean Architecture approach and now I want to unit-test the Use-Cases (which call Repositories, which call Data-Sources, so on and so on) against mocked API-data to make sure that the call-and-response-flow works throughout all layers (leaving the UI aside - will be covered in UI- & E2E-Tests).

Problem/Question:
When unit-testing a Use-Cases, there are multiple API-calls involved (for example the first one to refresh an access token and the second one to actually get the desired content). Therefore, I need a way define which responses should be returned for which requests.

So, this is my test case to get available offers from the marketplace:

it("successfully receives available offers", async () => {
      // Arrange
      fetch.mockResponse((req) => {
          if (req.url.endsWith('/connect/authorize')) {
              return Promise.resolve(JSON.stringify({
                  id_token: "",
                  refresh_token: "",
                  access_token: ""
              }));
          } else if (req.url.endsWith("/api/Offers")) {
              return Promise.resolve(JSON.stringify([{
                  id: "1",
                  name: "Test",
                  description: "Test",
                  location: "Goslar"
              }]));
          }
          return Promise.resolve("");
      });
      const getAvailableOffers = new GetAvailableOffers(new OfferRepository(new OfferApiDatasource(new BlueprintOffersApiClient())));

      // Act
      const result = await getAvailableOffers.invoke();

      // Assert
      expect(result.length).toBe(0);
  });

Test-Result:
The result of this is the following error being thrown in the OfferRepository:

Get available offers › successfully receives available offers
    FetchError: invalid json response body at  reason: Unexpected end of JSON input
      at node_modules/node-fetch/lib/index.js:273:32

What should happen:
By calling the GetAvailableOffers-Use-Case, the BlueprintOffersAPIClient requests a new valid access token. For this, the first Promise should be received. After that, all available offers should be fetched and the second Promise should be received.

Specific question:
How can I specify mocked responses for URLs/individual requests, so that when running my test-case my token-refresh-request receives the /connect/authorize mocked response, but my get-available-offers-request receives the mocked response for /api/Offers?

@warrenronsiek
Copy link

warrenronsiek commented Jun 23, 2024

I have this exact problem. Very surprised that this use case hasn't come up. Seems like the API encourages you to do this with chaining mockIf and doMockIf but they dont work. I.e. the follow test fails:

test('mock multiple endpoints', async () => {
  const overviewResponse = { data: 'overview data' };
  const summaryResponse = { summary: 'a summary' };

  fetchMock
    .doMockIf(/https:\/\/foo\.com\/api1/, req => {
      return Promise.resolve({
        status: 200,
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(overviewResponse),
      });
    })
    .doMockIf(/https:\/\/foo\.com\/api2/, req => {
      return Promise.resolve({
        status: 200,
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(summaryResponse),
      });
    });

  const overviewResult = await fetch('https://foo.com/api1');
  const overviewJson = await overviewResult.json();
  expect(overviewJson).toEqual(overviewResponse);

  const summaryResult = await fetch('https://foo.com/api2');
  const summaryJson = await summaryResult.json();
  expect(summaryJson).toEqual(summaryResponse);
});

Which is counter intuitive as making this work seems like the whole point of having a chainable mockIf. Will update if I figure it out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants