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

Mocking with Mock Service Worker (MSW) #1584

Open
baxford opened this issue Mar 22, 2023 · 2 comments
Open

Mocking with Mock Service Worker (MSW) #1584

baxford opened this issue Mar 22, 2023 · 2 comments

Comments

@baxford
Copy link

baxford commented Mar 22, 2023

Hi, first of all, thanks for this amazing library, I find it useful both serverside and in React apps!
This is my first post here, so please let me know if I can provide more info or code samples.

I have been looking into ways of mocking gqty in test cases, and have tried a few different options.
My first attempt was to mock the gqty generated client with Jest and then replace the implementations of query / mutation etc.
jest.mock('../src/gqty', () => ({

This works to some extent but my tests aren't as real-world as I would like them to be.

My latest efforts are to use Mock Service Worker, which is partially working for me, with the following conditions:

  • gqty uses anonymous operations so MSW graphql.query an graphql.mutation options are not available, instead I had to use graphql.operation.. See https://mswjs.io/docs/getting-started/mocks/graphql-api
  • gqty appears to use aliases (I think for caching purposes) when building the graphql commands, so the graphql.operation needs to run regexes on the query and match preconfigured operation names.
  • jsonb fields are also aliased within the query (I guess also for caching purposes), so these would need some regex/pattern matching to resolve (I haven't done this yet)

The following code addresses the first two points above but not the third. I think it would be possible to address the third but it feels quite complex, and I am wondering if there might be another way to solve this.

export const MOCK_GRAPHQL_API = 'http://test-graphql.api/'

jest.mock('next/config', () => () => ({
  publicRuntimeConfig: {
    publicGraphqlUrl: MOCK_GRAPHQL_API
  }
}))
import { SetupServer, setupServer } from "msw/node";
import { RequestHandler, graphql } from "msw";

const mockGraphqlAPI = graphql.link(MOCK_GRAPHQL_API);

export interface MockGraphqlResponse {
  type: string;
  operation: string;
  response: object | object[];
}

let server: SetupServer;

export const mockAPI = (mockResponses: MockGraphqlResponse[]) => {
  const handlers: Array<RequestHandler> = [
    mockGraphqlAPI.operation(async (req, res, ctx) => {
      const m = await req.json()
      // console.log(m)
      const matchedOp = mockResponses.reduce((acc, {type, operation, response}) => {
        // It appears that gqty uses dynamic aliases for mutations/queries.
        // So we use a regex to find the alias and return the given data for the alias
        const typeRe = new RegExp('^(' + type + ')\\(');
        const typeMatches = m.query.match(typeRe)
        const operationRe = new RegExp('\\{(' + operation + '_.*)\:' + operation + '\\(');
        const opMatches = m.query.match(operationRe)
        if (typeMatches && opMatches) {
          const alias = opMatches[1];
          const data = {} as any;
          data[alias] = response;
          acc = { data }
        }
        return acc;
      }, null as any)

      if (matchedOp?.data) {
        return res(ctx.data(matchedOp.data));
      } else {
        return;
      }
    })
  ];


  server = setupServer(...handlers);
  server.listen({
    onUnhandledRequest: 'error',
  })
}

export const closeMockServer = () => {
  server?.close();
}

The above can then be used in test cases as follows:

//Define mock responses for the Graphql operations.
const mockAPIResponses = [{
  type: 'query',
  operation: 'user_by_pk',
  response: {
    id: 'userId123',
    name: 'Test User',
    email: '[email protected]',
  }
}];
beforeEach(() => {
  mockAPI(mockAPIResponses);
})
afterEach(() => {
  closeMockServer();
})

This allows my react test cases to fully test the gqty code within the rendering, however it feels a little clunky.

I'm wondering if anyone here knows of a better way of using MSW for this type of testing?
If it was possible to override the aliasing or switch it off in the test cases it may simplify things.

@vicary
Copy link
Member

vicary commented Mar 22, 2023

Operation names are added in canary for some hooks, and it is supported for all API in the upcoming new core as we strive towards a more complete support on the mocking side.

For a complete server mocking, we uses graphql-ez ourselves in tests. You may not need it right now, but when your projects grows to that point, please do take a look on our tests for reference!

@baxford
Copy link
Author

baxford commented Mar 27, 2023

thanks @vicary for this info, I will take a look at the tests!

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