meeshkan / unmock-js

Fuzz test your REST API calls
https://unmock.io
93 stars 8 forks source link

Error: no matcher found when mocking same endpoints, differing in query param #404

Closed lucasmerker closed 4 years ago

lucasmerker commented 4 years ago

Scenario: Due to a very poorly built API (provided by enterprise), I need to use the same REST endpoint to receive different data sets. The REST request only differs in one query parameter.

Here's the issue: If I use unmock to mock the same request path several times, which only differs in the query parameter, the test runs into an error (unmock error: Cannot find matcher for this request). As soon as I rename the endpoint, the mock works as usual.

Expected: Matching the mocks works while the query just differs in query params.

Minimal reproducible example:

import unmock, { Service, transform, sinon, u, Arr } from "unmock";
import Axios from "axios";

let exampleService: Service;

unmock
  .nock("https://www.example.org:443/", "EXAMPLE")
  .get("/exampleEndpoint/")
  .query({
    selector: u.regex("Details") 
    // same error with: u.cnst("Details), "Details"
  })
  .reply(200, {
    Hello: "World"
  })
  .get("/exampleEndpoint/")
  .query({
    selector: u.regex("Summary")
  })
  .reply(200, {
    Bella: "Ciao"
  });

beforeAll(() => {
  exampleService = unmock.on().services.EXAMPLE;
});
beforeEach(() => {
  exampleService.reset();
});
afterEach(() => {
  exampleService.spy.resetHistory();
});
afterAll(() => {
  if (exampleService) unmock.off();
});

it("Example test", async () => {
  //exampleService.state(transform.withCodes(200));
  await Axios.get(
    "https://www.example.org/exampleEndpoint?selector=Details"
  ).then(e => console.log(e.data));
});

PS. Thanks for your work! We love your library. <3

ksaaskil commented 4 years ago

Hi @Difreg, thanks for the issue and thanks a lot for the reproducible example! You seem to have found a bit tricky case indeed: Because unmock keeps state internally in OpenAPI-like objects, where endpoints are stored in a dictionary, your first definition indeed seems to be overwritten by the second one. We'll have to think a bit what the correct solution is and get back to you!

lucasmerker commented 4 years ago

Hey @ksaaskil, thank you very much for the fast reply! My colleague @jonasgrunert and I are available for joint considerations :)

mikesol commented 4 years ago

Hi Difreg! As @ksaaskil mentioned, there are certain cases that unmock can't handle yet, and this is unfortunately one of them. There is a way to do it, though. We should document it better, @carolstran and I can have a think about how to best do this. Below is the way to implement your example case in unmock.

import unmock, { Service, transform, sinon, u, Arr } from "unmock";
import Axios from "axios";

let exampleService: Service;

unmock
  .nock("https://www.example.org:443/", "EXAMPLE")
  .get("/exampleEndpoint/")
  .reply(200, {});

beforeAll(() => {
  exampleService = unmock.on().services.EXAMPLE;
});
beforeEach(() => {
  exampleService.reset();
});
afterEach(() => {
  exampleService.spy.resetHistory();
});
afterAll(() => {
  if (exampleService) unmock.off();
});

it("Example test", async () => {
  exampleService.state((req, spec) =>
    transform
      .responseBody({
        path: "/exampleEndpoint/",
      })
      .const(
        req.query.selector == "Details"
          ? { Hello: "World" }
          : req.query.selector == "Summary"
          ? { Bella: "Ciao" }
          : { Not: "Defined" }
      )(req, spec)
  );
  await Axios.get(
    "https://www.example.org/exampleEndpoint?selector=Details"
  ).then((e) => console.log(e.data));
  await Axios.get(
    "https://www.example.org/exampleEndpoint?selector=Summary"
  ).then((e) => console.log(e.data));
  await Axios.get(
    "https://www.example.org/exampleEndpoint?selector=FooBar"
  ).then((e) => console.log(e.data));
});
mikesol commented 4 years ago

I'll close this for now, but please reopen if there's an issue with the above code.