Update on 12 Sept 2018:
React Apollo introduced official documentation on testing in June 2018:
https://www.apollographql.com/docs/guides/testing-react-components.html
You can still read my original post below, which includes Enzyme specific techniques that is not covered in official docs.
Despite its popularity, React Apollo client has no good documentations on how to do testing for React Apollo components with Enzyme.
The only documentation that I found online for testing React Apollo component is a comment from React Apollo team on GitHub, and it is not using Enzyme.
So how do you test React Apollo components with Enzyme? And perhaps more importantly how do you get access to the inner component wrapped by React Apollo as a higher-order component (HOC) to mock its functions and verify its behaviours?
Method 1: Using React Apollo’s MockedProvider (HOC)
If you are following the guide for React Apollo query when writing your components, you probably have something like this in the file for your component:
1 2 3 4 5 6 7 |
class MyComponent extends Component { render() { // ... } } export default graphql(gql`query { ... }`)(MyComponent); |
By default, this would export the higher-order component wrapped with Apollo’s GraphQL query. This allows the inner component to have access to the query result via props.data.
If you just want to test the higher-order component with the React Apollo logic, you can use the MockedProvider inside react-apollo/test-utils package. Here is an example of how the test file would look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import React from 'react'; import { mount } from 'enzyme'; import toJSON from 'enzyme-to-json'; import { MockedProvider } from 'react-apollo/test-utils'; import { addTypenameToDocument } from 'apollo-client'; import MyComponent from 'somewhere'; import APPS_GQL from 'somewhere/apps.gql'; const appList = { apps: [ { name: 'React', __typename: 'App' }, { name: 'Angular', __typename: 'App' }, ], }; describe('MyComponent', () => { test('renders with data correctly', () => { const wrapper = mount( <MockedProvider mocks={[{ request: { query: addTypenameToDocument(APPS_GQL), variables: { myVariable: 'some value' }, }, result: { data: appList, }, }]} > <MyComponent myProp={someVariable} /> </MockedProvider>, ); expect(wrapper.find('MyComponent').prop('data').apps.length).toBe(2); expect(toJSON(wrapper)).toMatchSnapshot(); }); }); |
A few things to note here:
- Mocked Queries: You can mock any number of queries and the responses with MockedProvider. You just need to make sure that they match the queries you send in the export default graphql(gql`query { ... }`)(MyComponent) statement. There aren’t much documentation on how to use MockedProvider but there is a gist here with some examples.
- Inner Component: You can access the inner component via wrapper.find('MyComponent') and do things like accessing the props. However, you cannot use certain Enzyme helpers such as .state() because they only work on the root component .You can refer to the documentation from Enzyme on how which ones can be used and which ones cannot be used.
- Enzyme Mount vs Shallow: You can only use mount here, but not shallow because using shallow skips the React component life cycles including componentWillReceiveProps, which is used by React Apollo to pass in the response as props.data.
- Execution Time: This method is slow (100ms+ for me) because it takes time to do a full render using mount on jsdom.
Method 2: Export Plain Component Separately from HOC
Instead of just exporting the HOC in your component file, you can additionally export the plain component, along with the HOC. An example on how to do that was provided by the React Apollo team in this comment.
Apply this technique to rewrite MyComponent file in method 1, we have:
1 2 3 4 5 6 7 |
export class MyComponent extends Component { render() { // ... } } export default graphql(gql`query { ... }`)(MyComponent); |
Notice that we are exporting both MyComponent , and the HOC wrapped by React Apollo.
In order to import both the default export and the named export of the component, you can do:
1 |
import MyComponentWithGQL, { MyComponent } from 'somewhere'; |
Where MyComponentWithGQL is the HOC, and MyComponent is the plain component. If you are just importing MyComponent for the test, you can ignore the first part.
With the separately exported plain component, we can test it directly without the MockedProvider wrapper and set the props manually:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import React from 'react'; import { shallow } from 'enzyme'; import toJSON from 'enzyme-to-json'; import { MyComponent } from 'somewhere'; describe('MyComponent', () => { test('renders with data correctly', () => { const wrapper = shallow(<MyComponent myProp={someVariable} />); wrapper.setProps({ data: appList, }); expect(toJSON(wrapper)).toMatchSnapshot(); }); }); |
Compared to the first method, there are a few key differences:
- Isolation: Here we are testing the component directly without being wrapped in a HOC, hence the logic is simpler without the need to use MockedProvider. You can also access the component’s state via Enzyme’s .state() helper since the component is the root component.
- Enzyme Mount or Shallow: This method of testing allows us to use either mount or shallow since we are setting the props manually. Enzyme allows setProps to trigger componentWillReceiveProps even when using shallow, this makes the tests run faster. If you want to test child components within the component, you can still use mount.
- Execution Time: This is faster than the first method if you are using shallow, since lifecycle hooks are skipped and child components are not rendered.
Conclusion
So that was two ways to test React Apollo components using Enzyme. The test framework I used here is jest, but the idea should be applicable to other frameworks as well.
I personally prefer the second method of testing because it is simpler to write test. However, the first method does allow some form of integration testing for the component, so it might be useful in certain cases as well.
Cover image from https://medium.com/@reyhansofian/react-native-with-react-apollo-df8fd712f129
Hi, I used MockedProvider in test script of react-apollo component. but I am unable to populate data to component. here is my issue: https://stackoverflow.com/questions/48144412/reactjs-jest-unable-to-populate-react-apollo-component-with-data-using-mocke
Answered the question, please check. 🙂
Yes, I checked your answer Thanks for reply. I had done those changes. But still not receiving mocked data back as response.
Eager to see some results with this article and the comment section
Hey paradite, I am having the same issue with not seeing the data being populated to my component, my code is here:
https://github.com/sourcier/sourcier-page/blob/master/src/components/app/App.test.js
Your component is not wrapped with React Apollo, hence it does not understand the mock, you need to export your component with React Apollo wrapper, like
export default graphql(gql
query { … })(MyComponent)
, the query should match the one you are using in the MockedProvider.The App component is not wrapped with a React Apollo wrapper, but the child component I am trying to provide mock data to is: https://github.com/sourcier/sourcier-page/blob/master/src/components/posts/PostList.js
Does the provider only set the data on the immediate component it wraps? I have tested the individual components directly, but I would like to test the whole app as well if possible.
Yes, looks like it only works if the immediate wrapped component calls GraphQL, I am not sure how React Apollo passes down the client to child components or if it is supported. In your case, you are trying to integration test, which is not really supported by MockedProvider, maybe you can try mocking network call using
mockNetworkInterface
.