DEV Community

Cover image for Integration Tests with Relay
Sibelius Seraphini for Woovi

Posted on

Integration Tests with Relay

What to test in the frontend? was a success.

In this article, I will show some practical examples of how to test Frontend when using React and Relay.

Asserting and mocking GraphQL Requests using Relay

When testing a React app that uses Relay, we need to mock GraphQL requests that would be sent to our GraphQL server. We don't want to reach the real server, as we are doing integration tests, and e2e is very slow.

We prefer to reduce the scope of our test, and assume the GraphQL server will behave correctly, so we can focus on test frontend behavior.

Let's test this App component

const App = () => {
  const response = useLazyLoadQuery<AppQuery>(
    graphql`
      query AppQuery {
        me {
          name
        }
      }
    `,
    {},
    {},
  );

  return (
    <span>{response.me.name}</span>
  )
}
Enter fullscreen mode Exit fullscreen mode

This test will mock the AppQuery GraphQL request, it will await the request to be resolved by Relay, and it will finally assert the DOM has the value returned in the me { name } field.

Firstly, we need to create a mock environment, that will let us mock GraphQL requests easily.

import { createMockEnvironment } from 'relay-test-utils';

const environment = createMockEnvironment();
Enter fullscreen mode Exit fullscreen mode

We then render our component using @testing/library

import { render } from '@testing-library/react';

render(<App />);
Enter fullscreen mode Exit fullscreen mode

Before asserting anything in the DOM, we need to assert and mock the AppQuery request

We can assert that App component made the AppQuery request like this

assertOperation(environment, 'AppQuery', {});
Enter fullscreen mode Exit fullscreen mode

We can resolve/mock the request like this:

const mockResolvers = {
  User: () => ({ name: 'Woovi' }),
};

resolveMostRecentOperation(environment, mockResolvers);
Enter fullscreen mode Exit fullscreen mode

We then await the loading View to be removed from the DOM, so we know the request was resolved

await waitLoading();
Enter fullscreen mode Exit fullscreen mode

We validate that there are no more pending requests to be mocked

assertNoPendingOperations(environment);
Enter fullscreen mode Exit fullscreen mode

And we finally assert the DOM has the value that was mocked in our request

expect(screen.getByText('Woovi')).toBeTruthy();
Enter fullscreen mode Exit fullscreen mode

Relay test helpers

The above code uses some helpers to make testing Relay more declarative, lets show how they are implemented here:

assertOperation - assert operation name and variables

type OperationType = {
  variables: Record<string, unknown>;
};

export const assertOperation = <T extends OperationType>(
  environment: RelayMockEnvironment,
  name: string,
  variables: T['variables'] = {},
) => {
  const queryOperation = environment.mock.getMostRecentOperation();

  const operationName = getOperationName(queryOperation);
  const operationVariables = getOperationVariables(queryOperation);

  expect(operationName).toBe(name);
  expect(operationVariables).toEqual(variables);
};
Enter fullscreen mode Exit fullscreen mode

resolveMostRecentOperation - mock the most recent operation

export const resolveMostRecentOperation = (
  environment: RelayMockEnvironment,
  customMockResolvers: MockResolvers,
) => {
  act(() => {
    environment.mock.resolveMostRecentOperation((operation) => {     
      return MockPayloadGenerator.generate(operation, customMockResolvers);
    });
  });
};
Enter fullscreen mode Exit fullscreen mode

waitLoading - await loading element to be removed

export const waitLoading = async () => {
  await waitForElementToBeRemoved(screen.queryByTestId('loading'), {
    timeout: 20000,
  });
}
Enter fullscreen mode Exit fullscreen mode

In Conclusion

Testing Frontend with React and Relay can add some complexity to your tests, but if you create declarative helpers it can be very intuitive and easy.


Woovi
Woovi is a Startup that enables shoppers to pay as they like. To make this possible, Woovi provides instant payment solutions for merchants to accept orders.

If you want to work with us, we are hiring!


Photo by Roman Mager on Unsplash

Top comments (4)

Collapse
 
arthursady profile image
Arthur Sady

Have you guys ever used the queueOperationResolver to treat the mocked queries instead of having to call the resolveMostRecentOperation every time?

Collapse
 
sibelius profile image
Sibelius Seraphini

it could work as well

Collapse
 
arthursady profile image
Arthur Sady

Yeah, I was trying it out but my expectation was that the queueOperationResolver would trigger the mocking resolver for any query that would be added to the queue, but for me it only works for the first query. So for tests which depend on more than one query, it always times out on the second. And the docs only have examples with a single query. I even added a stack overflow question stackoverflow.com/q/79200010/7196423 but no luck so far.

Thread Thread
 
sibelius profile image
Sibelius Seraphini

I think queueOperationResolver is more about preload queries