Skip to content

Latest commit

 

History

History
88 lines (70 loc) · 3.09 KB

mock-external-services.md

File metadata and controls

88 lines (70 loc) · 3.09 KB

Mock responses of external HTTP services



One Paragraph Explainer

Isolate the component under test by intercepting any outgoing HTTP request and providing the desired response so the collaborator HTTP API won't get hit. Nock is a great tool for this mission as it provides a convenient syntax for defining external services behavior. Isolation is a must to prevent noise and slow performance but mostly to simulate various scenarios and responses - A good flight simulator is not about painting clear blue sky rather bringing safe storms and chaos. This is reinforced in a Microservice architecture where the focus should always be on a single component without involving the rest of the world. Though it's possible to simulate external service behavior using test doubles (mocking), it's preferable not to touch the deployed code and act on the network level to keep the tests pure black-box. The downside of isolation is not detecting when the collaborator component changes and not realizing misunderstandings between the two services - Make sure to compensate for this using a few contract or E2E tests.



Code Example – a simple mock using nock

// Intercept requests for internal or 3rd party APIs and return a predefined response
beforeEach(() => {
  nock("http://localhost/user/").get(`/1`).reply(200, {
    id: 1,
    name: "John",
  });
});

Code Example – simulating an important scenario inside the test

// Using an uncommon user id (7) and create a compatible interceptor
test("When the user does not exist, return http 404", async () => {
  //Arrange
  const orderToAdd = {
    userId: 7,
    productId: 2,
    mode: "draft",
  };

  nock("http://localhost/user/").get(`/7`).reply(404, {
    message: "User does not exist",
    code: "nonExisting",
  });

  //Act
  const orderAddResult = await axiosAPIClient.post("/order", orderToAdd);

  //Assert
  expect(orderAddResult.status).toBe(404);
});

Code Example – preventing requests from going outside to the real-world

beforeAll(async () => {
  // ...
  // ️️️Ensure that this component is isolated by preventing unknown calls
  nock.disableNetConnect();
  // Enable only requests for the API under test
  nock.enableNetConnect("127.0.0.1");
});

Code Example – ensuring that the outgoing request schema is correct

// ️️️Assert that the app called the mailer service appropriately with the right input
test("When order failed, send mail to admin", async () => {
  //Arrange
  // ...
  let emailPayload;
  nock("http://mailer.com")
    .post("/send", (payload) => ((emailPayload = payload), true))
    .reply(202);
  const orderToAdd = {
    userId: 1,
    productId: 2,
    mode: "approved",
  };

  //Act
  await axiosAPIClient.post("/order", orderToAdd);

  // ️️️Assert
  expect(emailPayload).toMatchObject({
    subject: expect.any(String),
    body: expect.any(String),
    recipientAddress: expect.stringMatching(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/),
  });
});