Testing Relay with Preloaded Queries
Components that use preloaded queries (useQueryLoader and usePreloadedQuery hooks) require slightly different and more convoluted test setup.
In short, there are two steps that need to be performed before rendering the component
- Configure the query resolver to generate the response via 
environment.mock.queueOperationResolver - Record a pending queue invocation via 
environment.mock.queuePendingOperation 
Symptoms that something is wrong​
- The test doesn't do what is expected from it.
 - The query seems to be blocking instead of executing
- E.g. the 
Suspenddoesn't switch from "waiting" to "data loaded" state 
 - E.g. the 
 - If you add the 
console.logbefore and afterusePreloadedQuery, only the "before" call is hit 
TL;DR​
const {RelayEnvironmentProvider} = require('react-relay');
const {MockPayloadGenerator, createMockEnvironment} = require('relay-test-utils');
const {act, render} = require('@testing-library/react');
test("...", () => {
  // arrange
  const environment = createMockEnvironment();
  environment.mock.queueOperationResolver(operation => {
      return MockPayloadGenerator.generate(operation, {
        CurrencyAmount() {
          return {
            formatted_amount: "1234$",
          };
        },
      });
    });
  const query = YourComponentGraphQLQueryGoesHere; // can be the same, or just identical
  const variables = {
    // ACTUAL variables for the invocation goes here
  };
  environment.mock.queuePendingOperation(YourComponentGraphQLQuery, variables);
 // act
  const {getByTestId, ..otherStuffYouMightNeed} = render(
    <RelayEnvironmentProvider environment={environment}>
        <YourComponent data-testid="1234" {...componentPropsIfAny}/>
    </RelayEnvironmentProvider>
  );
  // trigger the loading - click a button, emit an event, etc. or ...
  act(() => jest.runAllImmediates()); // ... if loadQuery is in the useEffect()
  // assert
  // your assertions go here
});
Configure the query resolver to generate the response​
This is done via environment.mock.queueOperationResolver(operation) call, but getting it right might be tricky.
The crux of this call is to return a mocked graphql result in a very particular format (as MockResolvers type, to be precise). This is done via a second parameter to generate - it is an object, whose keys are GraphQL types that we want to mock. (See mock-payload-generator).
Continuing on the above example:
return MockPayloadGenerator.generate(operation, {
  CurrencyAmount() { // <-- the GraphQL type
    return {
      formatted_amount: "response_value" <-- CurrencyAmount fields, selected in the query
    };
  }
});
The tricky thing here is to obtain the name of the GraphQL type and fields to return. This can be done in two ways:
- Call 
console.log(JSON.stringify(operation, null, 2))and look for theconcreteTypethat corresponds to what we want to mock. Then look at the siblingselectionsarray, which describes the fields that are selected from that object. 
It is possible to return different data for different query variables via Mock Resolver Context. The query variables will be available on the context.args, but only to the innermost function call (for the query above, only offer_ids are available)
CurrencyAmount(context) {
  console.log(JSON.stringify(context, null, 2)); // <--
  return { formatted_amount: mockResponse }
}
// <-- logs { ...snip..., "name": "subtotal_price_for_offers", args: { offer_ids: [...] } }
Record a pending queue invocation​
This is more straightforward - it is done via a call to environment.mock.queuePendingOperation(query, variables)
Queryneeds to match the query issues by the component. Simplest (and most robust against query changes) is to export the query from the component module and use it in the test, but having an identical (but not the same) query works as well.variableshas to match the variables that will be used in this test invocation.- Beware of nested objects and arrays - they are compared via 
areEqual(invocation code)- Arrays are compared by values (not by reference), but the order of elements matter
 - Nested objects - performs deep compare, order of keys is not relevant (this is not confirmed - please update this doc if you used a graphql query with "deep" structure)
 
 
- Beware of nested objects and arrays - they are compared via 
 
Troubleshooting​
console.log,console.logeverywhere! Recommended places:- component: before and after 
useQueryLoader, usePreloadedQuery, loadQuery - test: in 
queueOperationResolvercallback - library: in 
RelayModernMockEnvironment.execute, after theconst currentOperation = ...call (here) 
- component: before and after 
 - If 
loadQueryis not called - make sure to issue the triggering event. Depending on your component implementation it could be a user-action (like button click or key press), javascript event (via event emitter mechanisms) or a simple "delayed execution" withuseEffect.- The 
useEffectcase is probably easiest to miss - make sure to callact(() => jest.runAllImmediates())after rendering the component 
 - The 
 - If "before" 
usePreloadedQueryis hit, but "after" is not - the query suspends. This entire guide is written to resolve it - you might want to re-read it. But most likely it is either:- Used a different query - the query resolver would not be called, 
currentOperationwill benull - Query variables don't match - the query resolver would not be called, 
currentOperationwill benull(make sure to inspect thevariables).- Also, make sure arrays are in the same order, if any (or better yet, use sets, if at all possible).
 
 
 - Used a different query - the query resolver would not be called, 
 - If data returned from the query is not what you expect, make sure you're generating the right graphql type.
- You can tell you're mocking the wrong one if the return values look something like 
<mock-value-for-field-"formatted_amount"> 
 - You can tell you're mocking the wrong one if the return values look something like 
 
Make sure the component and the test use the same environment (i.e. there's no <RelayEnvironmentProvider environment={RelayFBEnvironment}> somewhere nested in your test React tree.
Epilogue​
Examples here use testing-library-react, but it works with the react-test-renderer as well.
Is this page useful?
Help us make the site even better by answering a few quick questions.