I am experimenting with React + Relay + Graphql these days. Unfortunately, I cannot find any easy and convenient way to test React component wrapped by Relay Container.
Basically, I would like to achieve these goals along TDD,
Compared with React + Flux, React + Relay is more like black box, or, declarative.
I can see people mock Relay.createContainer to bypass Relay and merely test React Component. It leaves the Relay part uncovered and there is no way to drive this part by testing. https://github.com/facebook/relay/issues/161
Also, I read through test cases of Relay and its really tedious to render a mock container. https://github.com/facebook/relay/blob/master/src/tools/mocks/RelayTestUtils.js
I will be really grateful if you can share you solution.
Thanks!
I've been trying to test Relay containers like I would components in a Flux application. Specifically, I want to make sure that they render the correct content for a given state and props and that they call methods to change data in appropriate places; in Flux this is a call to an action creator, in Relay this is a call to Relay.Store.update
or this.props.relay.setVariables
.
My first attempt was to build a RelayTestUtil
object with a renderContainerIntoDocument
method. I based it heavily on https://github.com/facebook/relay/blob/master/src/tools/mocks/RelayTestUtils.js, https://github.com/facebook/relay/blob/master/src/legacy/store/mocks/GraphQLStoreQueryResolver.js, and the Relay Container tests. This used very minimal mocking and was great for testing container rendering but was completely useless for testing data changes. Trying to spy on calls to Relay.Store.update
and this.props.relay.setVariables
, or to mock data changes, became more trouble than it was worth.
I settled on adding __mocks__\react-relay.js
to completely mock Relay and using simpler version of RelayTestUtils.renderContainerIntoDocument
to inject Relay properties into a container. I'm not entirely satisfied with this solution, but it seems to work for now.
__mocks__\react-relay.js
:
var Relay = require.requireActual('react-relay');
var React = require('react');
module.exports = {
QL: Relay.QL,
Mutation: Relay.Mutation,
Route: Relay.Route,
Store: {
update: jest.genMockFn()
},
createContainer: (component, containerSpec) => {
const fragments = containerSpec.fragments || {};
// mock the static container methods
Object.assign(component, { getFragment: (fragmentName) => fragments[fragmentName] });
return component;
}
};
RelayTestUtils.js
:
const React = require('react');
const ReactDOM = require('react-dom');
const RelayTestUtils = {
renderContainerIntoDocument(containerElement, relayOptions) {
relayOptions = relayOptions || {};
const relaySpec = {
forceFetch: jest.genMockFn(),
getPendingTransactions: jest.genMockFn().mockImplementation(() => relayOptions.pendingTransactions),
hasOptimisticUpdate: jest.genMockFn().mockImplementation(() => relayOptions.hasOptimisticUpdate),
route: relayOptions.route || { name: 'MockRoute', path: '/mock' },
setVariables: jest.genMockFn(),
variables: relayOptions.variables || {}
};
return ReactDOM.render(
React.cloneElement(containerElement, { relay: relaySpec }),
document.createElement('div')
);
}
};
export default RelayTestUtils;
Tests look something like this, where fragmentData
matches the shape of the GraphQL response:
it('changes items', () => {
const myContainer = RelayTestUtils.renderContainerIntoDocument(
<MyContainer { ...fragmentData }/>,
{ variables: { itemId: 'asdf' } }
);
myContainer.changeItem();
expect(myContainer.props.relay.setVariables).toBeCalled();
});
I've wrote a blog post of how to test Relay using Jest against a working GraphQL backend: https://medium.com/entria/relay-integration-test-with-jest-71236fb36d44#.an74bdopy
This repo https://github.com/sibelius/relay-integration-test contains examples of testing react web and react native using Relay
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With