question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

I generate restful-react hooks from my openAPI spec. I’ve been looking into various ways of handling mocks and wanted to open a discussion about the typesafe solution I’ve found.

I first tried using jest to mock the hooks themselves, but that became dangerously complicated. I eventually moved on to mocking http requests. This was clean in that I wasn’t usurping any of restful-react’s behavior. The downside to this methodology was that it “leaks”. Developers now need to be aware of url paths which the restful-react codegen had previously abstracted from them. For example, I’d include a line like this in my component test:

nock("http://localhost")
      .get(`/api/v1/rbac/users`)
      .reply(code, response);

This is pretty fragile, as well as not having typesafety of the response baked in- meaning the mock behavior in the test can easily deviate from real-world behavior of a changing api.

I decided to look into writing a snippet to codegen my mocks- using the same types that restful-react generates for me. Here’s an example of one of the mocks I now generate:

type GetUsersResponses = {
    200: RbacUsers;  
    404: UserNotFound 
} & {[code: number]: Error};

export function mockGetUsers<T extends keyof GetUsersResponses>(code: T,response: GetUsersResponses[T]) {
    return nock("http://localhost")
      .get(`/api/v1/rbac/users`)
      .reply(code, response);
}

This felt great. Not only am I able to use simple functions like mockGetUsers in my component tests, but they stay type safe and up to date with the rest of the codegen. Additionally, we keep the url path abstracted from the developers.

As an added cool feature, the response type is a dependent type on the status code, so the TS compiler will yell at you, for example: if you try to mock a 404 response to a 200 status code:

Screen Shot 2020-01-25 at 8 36 10 AM Screen Shot 2020-01-25 at 8 36 20 AM

Here’s my proof-of-concept nodejs script to pull this off for responses. https://gist.github.com/seanlaff/ee4a07682bc74e3e4077a0ee394e6b15

Maybe it’s worthwhile to consider something like this as a feature of restful-react?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
seanlaffcommented, Feb 24, 2020

Here’s my first pass at this for those that are interested. “Destructuring” the genericsString that is available to the generator was a little gymnastic-y @fabien0102 (I think I would have preferred them be individual params, rather than a comma delimmited string), but otherwise this is pretty powerful!

module.exports = {
  "hooks-gen": {
    file: "swagger.json",
    output: "hooks/index.tsx",
    customImport: `
import nock, { DataMatcherMap, InterceptFunction } from "nock";

// Destructure the generics string the codegen gives us
type DesResponseType<
  ResponseType,
  ErrorType,
  QueryParams,
  RequestBody = undefined
> = ResponseType;

type DesErrorType<
  ResponseType,
  ErrorType,
  QueryParams,
  RequestBody = undefined
> = ErrorType;

type DesQueryParams<
  ResponseType,
  ErrorType,
  QueryParams,
  RequestBody = undefined
> = QueryParams extends void ? {} : QueryParams;

type DesRequestBody<
  ResponseType,
  ErrorType,
  QueryParams,
  RequestBody = undefined
> = RequestBody;

type GenericResponseMap<ResponseType, ErrorType, QueryParams, RequestBody = undefined> = { 200: ResponseType } & { [code: number]: ErrorType };
type GenericResponse<ResponseType, ErrorType, QueryParams, RequestBody = undefined> = ResponseType | ErrorType

type mocker<ResponseType, ErrorType, QueryParams, RequestBody = undefined> = (
  code: number,
  response: ResponseType | ErrorType,
  pathParams: {},
  queryParams: QueryParams,
  request: RequestBody
) => nock.Scope;

type RequestBodyMatcher<T> = Exclude<Parameters<InterceptFunction>[1], ((body: any) => boolean)> | ((body: T) => boolean)

`,
    customGenerator: ({
      componentName,
      verb,
      route,
      description,
      genericsTypes,
      paramsInPath,
      paramsTypes,
      operation,
    }) => {
      var pathParamsAsFuncParams = paramsInPath.map(p => `${p}: string`);
      // Array of params for the generated function
      var funcParams = [...pathParamsAsFuncParams];
      if(paramsTypes !== ""){
        funcParams.push(`queryParams: DesQueryParams<${genericsTypes}>`)
      }
      funcParams.push("code: T");
      funcParams.push(`response: ${componentName}Responses[T]`);
      funcParams.push(
        `requestBodyMatcher?: RequestBodyMatcher<DesRequestBody<${genericsTypes}>>`
      );

      var nockVerb = `.${verb}(\`${route}\`, requestBodyMatcher)`;
      var nockChains = [nockVerb];
      if (paramsTypes !== "") {
        nockChains.push(".query(queryParams as DataMatcherMap)");
      }
      nockChains.push(".reply(code, response)");

      var output = `
type ${componentName}Responses = GenericResponseMap<${genericsTypes}>;
export function mock${componentName}<T extends keyof ${componentName}Responses>(${funcParams.join(
        ", "
      )}) {
    return nock("http://localhost")
        ${nockChains.join("\n        ")};
}`;
      return output;
    },
  },
};

This will produce mock funcs like

mockRbacGetUsers(200, { users: [fakeUser] });

while also doing dependent typing between the status code and the response body

0reactions
Zachecommented, Apr 3, 2020

How are you getting nock to work with fetch?

Read more comments on GitHub >

github_iconTop Results From Across the Web

GoMock is a mocking framework for the Go ... - GitHub
The mockgen command is used to generate source code for a mock class given a Go source file containing interfaces to be mocked....
Read more >
Mockaroo - Random Data Generator and API Mocking Tool ...
A free test data generator and API mocking tool - Mockaroo lets you create custom CSV, JSON, SQL, and Excel datasets to test...
Read more >
Mimicc – Mock generator for C and C++
Mimicc is a clang-based C/C++ compiler and mocking framework that turns function ... By fully automating the mock generation step, you can focus...
Read more >
EasyMock
EasyMock makes mocking easier. EasyMock has been the first dynamic Mock Object generator, relieving users of hand-writing Mock Objects, or generating code ...
Read more >
mock-generator - PyPI
Generate python mocks and assertions quickly. ... This tool is meant to save you time, by generating the Arrange and Assert sections for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found