Relay Integration Test with Jest

Sibelius Seraphini
Entria
Published in
4 min readNov 25, 2016

Unit testing is great, snapshot testing is also great, but from my practice the best tests are those that test both backend and fronted (Integration Test). — Grigory Ptashko

So how does a Relay integration test look like?

This test will make Relay containers make real requests to a working GraphQL backend.

Source code of this post: https://github.com/sibelius/relay-integration-test (both React Web and React Native, enjoy).

Working GraphQL Backend: https://github.com/sibelius/graphql-dataloader-boilerplate

Motivation

Relay integration tests are the simplest way to make sure you discover any broken component or code when you modify your GraphQL backend or your Relay frontend app with React or React Native

Way to make it work

This idea was proposed by https://github.com/GrigoryPtashko on these 2 issues: facebook/relay#1281 and facebook/jest#1898

I start digging into Jest testing and I realized the problem was that fetch is not polyfilled in Jest environment (facebook/jest#2071)

For React Native I need to polyfill XMLHttpRequest to make it work (sibelius/relay-integration-test)

For React web I need to polyfill fetch (sibelius/relay-integration-test)

The final tests looks like this:

import 'react-native';
import React from 'react';
import Relay from 'react-relay';
import App from '../app';
import RelayStore from '../RelayStore';

// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';

RelayStore.reset(
new Relay.DefaultNetworkLayer('http://localhost:5000/graphql')
);

const delay = (value) => new Promise(resolve =>
setTimeout(() => resolve(), value)
);

it('renders correctly', async () => {
const tree = renderer.create(
<App />
);
// Wait Relay to fetch data
await delay(3000);

expect(tree.toJSON()).toMatchSnapshot();
});

This test will render <App /> component that it is a Relay.Renderer container using react-test-renderer, then it will wait 3 seconds to let Relay fetch the required data, then we do a snapshot of the result component

Step By Step

  1. Add Jest to your project
yarn add jest jest-cli react-test-renderer --dev

For React web (polyfill of fetch)

yarn add isomorphic-fetch --dev

For React Native (react native jest preset and XMLHttpRequest polyfill)

yarn add jest-react-native xhr2 --dev

2. Add Jest config to your package.json

For React web

"jest": {
"setupFiles": [
"./test/env.js"
]
},
"scripts": {
"test": "jest"
}

For React Native

"jest": {
"preset": "jest-react-native",
"setupFiles": [
"./test/env.js"
]
},
"scripts": {
"test": "jest"
}

3. Create env.js file (setup test environment)

For React web

import 'isomorphic-fetch';

For React Native

const XMLHttpRequest = require('xhr2');

global.XMLHttpRequest = XMLHttpRequest;

4. Create a Relay Container component to test

import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
} from 'react-native';
import Relay from 'react-relay';
import ViewerQuery from './ViewerQuery';
import { createRenderer } from './RelayUtils';
import RelayStore from './RelayStore';

RelayStore.reset(
new Relay.DefaultNetworkLayer('http://localhost:5000/graphql')
);

class RelayApp extends Component {
render() {
return (
<View>
<Text>name: {this.props.viewer.users.edges[0].node.name}</Text>
<Text>length: {this.props.viewer.users.edges.length}</Text>
</View>
);
}
}

// Create a Relay.Renderer container
export default createRenderer(RelayApp, {
queries: ViewerQuery,
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
users(first: 2) {
edges {
node {
name
}
}
}
}
`,
},
});

createRenderer is a helper function that will create a Relay.Renderer easily for you (check the file here: https://gist.github.com/janicduplessis/f513032eb37cdde5d050d9ce8cf0b92a)

For web, I have adapted a version of createRenderer — https://github.com/sibelius/relay-integration-test/blob/master/RelayWeb/src/RelayUtils.js

5. Create the first Relay Integration Test

import 'react-native';
import React from 'react';
import Relay from 'react-relay';
import App from '../app';
import RelayStore from '../RelayStore';
import renderer from 'react-test-renderer';

RelayStore.reset(
new Relay.DefaultNetworkLayer('http://localhost:5000/graphql')
);

const delay = (value) => new Promise(resolve =>
setTimeout(() => resolve(), value)
);

it('renders correctly', async () => {
const tree = renderer.create(
<App />
);
// Wait Relay to fetch data
await delay(3000);

expect(tree.toJSON()).toMatchSnapshot();
});

6. Start your GraphQL server

I'm using graphql-dataloader-boilerplate (https://github.com/sibelius/graphql-dataloader-boilerplate) as my testing server. Just run the following command to make it running

yarn run watch

7. Start your tests

yarn test

or simpler:

jest

tip: when writing a test, you can use the following command to watch changes automatically in the file you are working on:

jest TestFileName --watch

Thanks to Grigory Ptashko to come up with this awesome idea.

What's Next

  • Testing Relay Mutations
  • Testing changing Relay variables behavior

--

--