Jest: Does jest support relay components?

Created on 24 Dec 2015  路  7Comments  路  Source: facebook/jest

I am testing a Relay.RootContainer component. However, it seems that jest does not look out the asynchronous data fetch of relay.

Here is the component.

class RootContainerComponent extends React.Component {
  <Relay.RootContainer
    Component={someContainerComponent}
    route={someRoute}
  />
}

Here is the test spec

const {
  renderIntoDocument,
  findRenderedDOMComponentWithTag,
  Simulate
} = require('react-addons-test-utils')

const rootContainerComponent = renderIntoDocument(
  <RootContainerComponent />
)

Simulate.click(findRenderedDOMComponentWithTag(rootContainerComponent, 'button'))

The component will fetch data from server when rendering. It seems to be the duty of jest to take care the asynchronous request.

Allows you to test asynchronous code synchronously.

But the last line of test codes executes before the component is fully rendered. And an error occurs.

Error: Did not find exactly one match for tag:button

Is there a workaround? Thanks.

Most helpful comment

If someone is interested in integration testing of Relay containers (I mean with real network requests and response against a working GraphQL backend) - I've managed to do it. Here's a simple test that works for me:

jest.mock('react/lib/ReactOwner'); // TODO: 褝褌芯 褍斜褉邪褌褜 懈谢懈 泻邪泻?
jest.unmock('react-relay');

import React from 'react'; // eslint-disable-line import/imports-first
import Relay from 'react-relay'; // eslint-disable-line import/imports-first
import TestUtils from 'react-addons-test-utils'; // eslint-disable-line import/imports-first

Relay.injectNetworkLayer(
  new Relay.DefaultNetworkLayer('http://localhost:10000/q')
);

describe('Me', () => {
  it('can make request to /q anyway', () => {
    class RootRoute extends Relay.Route {
      static queries = {
        root: (Component) => Relay.QL`
            query {
                root {
                    ${Component.getFragment('root')}
                }
            }
        `,
      };

      static routeName = 'RootRoute';
    }

    class AppRoot extends React.Component {
      static propTypes = {
        root: React.PropTypes.object.isRequired,
      };

      render() {
        expect(this.props.root).not.toBe(null);
        expect(this.props.root.me).not.toBe(null);
        expect(this.props.root.me.firstName).not.toBe(null);
        expect(this.props.root.me.authorities[0]).not.toBe(null);
        expect(this.props.root.me.authorities[0].authority).toEqual('ROLE_ANONYMOUS');

        return (
          <div>
            {this.props.root.me.authorities[0].authority}
          </div>
        );
      }
    }

    const AppContainer = Relay.createContainer(AppRoot, {
      fragments: {
        root: () => Relay.QL`
            fragment on Root {
                me {
                    firstName
                    email
                    authorities {
                        authority
                    }
                }
            }
        `,
      },
    });

    const container = TestUtils.renderIntoDocument(
      <Relay.Renderer
        Container={AppContainer}
        queryConfig={new RootRoute()}
        environment={Relay.Store}
        render={({ done, error, props, retry, stale }) => {
          if (error) {
            return <div>error</div>;
          } else if (props) {
            return <AppContainer {...props} />;
          } else {
            return <div>loading</div>;
          }
        }}
      />
    );

    return new Promise(resolve => setTimeout(() => resolve(), 1000));
  });
});

One of the key moments here is returning a promise from the test. This way Relay can make query and render its component with received data.
Hope this helps someone.

All 7 comments

This is a question, not an issue with jest. If you wan to test Relay components I would recommend not trying to render them using a root container but instead rendering the RelayContainer with fake data. Another solution is to write a mock for Relay's network layer to inject your own data.

Please use the jestjs-tag on StackOverflow to find answers to anything related to the usage of jest.

This issue tracker should mainly be used to report and discuss TECHNICAL issues. GitHub is simply not the right platform to offer support for products.

@leeching how did u solve ur issue?

@sibelius It seems that Relay.RootContainer component is not testable very well. So I do not test it finally.

cc @yuzhi do you know how to test a Relay.RootContainer properly in a test? :)

If someone is interested in integration testing of Relay containers (I mean with real network requests and response against a working GraphQL backend) - I've managed to do it. Here's a simple test that works for me:

jest.mock('react/lib/ReactOwner'); // TODO: 褝褌芯 褍斜褉邪褌褜 懈谢懈 泻邪泻?
jest.unmock('react-relay');

import React from 'react'; // eslint-disable-line import/imports-first
import Relay from 'react-relay'; // eslint-disable-line import/imports-first
import TestUtils from 'react-addons-test-utils'; // eslint-disable-line import/imports-first

Relay.injectNetworkLayer(
  new Relay.DefaultNetworkLayer('http://localhost:10000/q')
);

describe('Me', () => {
  it('can make request to /q anyway', () => {
    class RootRoute extends Relay.Route {
      static queries = {
        root: (Component) => Relay.QL`
            query {
                root {
                    ${Component.getFragment('root')}
                }
            }
        `,
      };

      static routeName = 'RootRoute';
    }

    class AppRoot extends React.Component {
      static propTypes = {
        root: React.PropTypes.object.isRequired,
      };

      render() {
        expect(this.props.root).not.toBe(null);
        expect(this.props.root.me).not.toBe(null);
        expect(this.props.root.me.firstName).not.toBe(null);
        expect(this.props.root.me.authorities[0]).not.toBe(null);
        expect(this.props.root.me.authorities[0].authority).toEqual('ROLE_ANONYMOUS');

        return (
          <div>
            {this.props.root.me.authorities[0].authority}
          </div>
        );
      }
    }

    const AppContainer = Relay.createContainer(AppRoot, {
      fragments: {
        root: () => Relay.QL`
            fragment on Root {
                me {
                    firstName
                    email
                    authorities {
                        authority
                    }
                }
            }
        `,
      },
    });

    const container = TestUtils.renderIntoDocument(
      <Relay.Renderer
        Container={AppContainer}
        queryConfig={new RootRoute()}
        environment={Relay.Store}
        render={({ done, error, props, retry, stale }) => {
          if (error) {
            return <div>error</div>;
          } else if (props) {
            return <AppContainer {...props} />;
          } else {
            return <div>loading</div>;
          }
        }}
      />
    );

    return new Promise(resolve => setTimeout(() => resolve(), 1000));
  });
});

One of the key moments here is returning a promise from the test. This way Relay can make query and render its component with received data.
Hope this helps someone.

@yuzhi ?

cc @yuzhi do you know how to test a Relay.RootContainer properly in a test? :)

Was this page helpful?
0 / 5 - 0 ratings