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();
});