Relay Integration Test with Jest
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
- 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
Resources
You should also unit test your GraphQL Backend
You should also make your GraphQL Backend Secure —
Checkout my GraphQL + DataLoader boilerplate with 95% of code coverage
Having trouble with React Native + Relay — check this starter code:
Trying to use ex-navigation with Relay — get up and running
Newsletter
Subscribe to my newsletter for new content https://sibelius.substack.com/