Enzyme: Has anyone tried using this with Relay?

Created on 17 May 2016  路  8Comments  路  Source: enzymejs/enzyme

I am trying to take advantage of this using Relay.

import TestUtils from 'react-addons-test-utils';
import React from 'react';
import Relay from 'react-relay';
import jsdom from 'mocha-jsdom';
import Footer from '../../configuration/webapp/components/Footer.jsx';
import {mount,swallow} from 'enzyme';



describe('Components Test', function() {
    describe('<Footer />', () => {

            jsdom();

        const fixtures = {
            Viewer: {
                User_IsAnonymous:true,
         }
    };

        it('calls componentDidMount', () => {
            const wrapper = mount(<Footer  />);
            expect(Footer.prototype.componentDidMount.calledOnce).to.equal(true);
        });

    });
});

Error 
  Invariant Violation: RelayContainer: `Relay(Footer)` was rendered with invalid Relay context `undefined`. Make sure the `relay` property on the React context conforms to the `RelayEnvironment` interface.
      at invariant (node_modules/fbjs/lib/invariant.js:38:15)
      at new RelayContainer (node_modules/react-relay/lib/RelayContainer.js:94:76)
      at ContainerConstructor (node_modules/react-relay/lib/RelayContainer.js:715:12)
      at [object Object].ReactCompositeComponentMixin._constructComponentWithoutOwner (node_modules/react/lib/ReactCompositeComponent.js:250:14)
      at [object Object].ReactCompositeComponentMixin._constructComponent (node_modules/react/lib/ReactCompositeComponent.js:236:21)
      at [object Object].ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:159:21)
      at [object Object].wrapper [as mountComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:39:35)
      at [object Object].ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:297:34)
      at [object Object].ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:222:21)
      at [object Object].wrapper [as mountComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:39:35)
      at [object Object].ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:297:34)
      at [object Object].ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:222:21)
      at [object Object].wrapper [as mountComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:39:35)
      at mountComponentIntoNode (node_modules/react/lib/ReactMount.js:103:32)
      at ReactReconcileTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:136:20)
      at batchedMountComponentIntoNode (node_modules/react/lib/ReactMount.js:124:15)
      at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:136:20)
      at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:63:19)
      at Object.batchedUpdates (node_modules/react/lib/ReactUpdates.js:97:20)
      at Object.ReactMount._renderNewRootComponent (node_modules/react/lib/ReactMount.js:277:18)
      at Object.wrapper [as _renderNewRootComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactMount._renderSubtreeIntoContainer (node_modules/react/lib/ReactMount.js:354:32)
      at Object.ReactMount.render (node_modules/react/lib/ReactMount.js:374:23)
      at Object.wrapper [as render] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactTestUtils.renderIntoDocument (node_modules/react/lib/ReactTestUtils.js:78:21)
      at renderWithOptions (node_modules/enzyme/build/react-compat.js:175:26)
      at new ReactWrapper (node_modules/enzyme/build/ReactWrapper.js:94:59)
      at mount (node_modules/enzyme/build/mount.js:21:10)
      at Context.<anonymous> (footer.js:24:29)



Any help or tips would be great!

question

Most helpful comment

We use enzyme and Relay together pretty well. We use jest, and mock out react-relay like so:

const Relay = require.requireActual('react-relay');

module.exports = {
  QL: Relay.QL,
  Mutation: Relay.Mutation,
  Route: Relay.Route,
  createContainer: component => component
};

I'd suggest also exporting the unconnected component (as @blainekasten suggested) alongside the default RelayContainer which you can then just pass in a mock viewer or relay object so you're testing your component's functionality instead of testing Relay.

All 8 comments

Based on https://github.com/facebook/relay/commit/bfb84bc5f2fc517d829737b6f2c9ba09474cdfc0

I think you need to provide relay as a property on context. Something like this:

import { Environment } from 'relay';
import RelayComponent from 'somewhere';
import { mount } from 'enzyme';

mount(
  <RelayComponent />,
  { context: { relay: new Environment } }
);

it's worth a try, not 100% on this. You might need to export the non-relay component

We use enzyme and Relay together pretty well. We use jest, and mock out react-relay like so:

const Relay = require.requireActual('react-relay');

module.exports = {
  QL: Relay.QL,
  Mutation: Relay.Mutation,
  Route: Relay.Route,
  createContainer: component => component
};

I'd suggest also exporting the unconnected component (as @blainekasten suggested) alongside the default RelayContainer which you can then just pass in a mock viewer or relay object so you're testing your component's functionality instead of testing Relay.

@Aweary , @blainekasten Thank you for your responses. I been trying to accomplish this with Mocha. This is a brand new project so I think I am going to switch to Jest.

@Aweary When you say unconnected component you mean the React component without the Relay Container? Do you know of anywhere that i could get a large example of a single component with its unit tests?

I am starting to think it would be better to have these imported separately. Having the container and component separated so you can import either or?
Thanks again!

When you say unconnected component you mean the React component without the Relay Container?

@rterysen-openroad correct, you can then pass in a mock relay / viewer prop.

Do you know of anywhere that i could get a large example of a single component with its unit tests?

I'm sorry I don't know of any offhand. All of our components are internal.

Having the container and component separated so you can import either or?

Yeah so it might look like:

export class Component extends React.Component {
 // ...
}


export default relay.createContainer(Component, {/* ... */})

And then you can import either


import {Component}, ConnectedComponent from 'your/component'

@rterysen-openroad I'm going to close this issue out as there isn't really anything actionable for us here, but feel free to continue discussing the topic!

@Aweary Thank you very much. You where extremely helpful!!!!

@rterysen-openroad happy to help 馃槃

I just spent some time getting this working, and here's what I came up with. It replaces Relay.createContainer with a version that can be disabled via Relay.createContainer.disable() during tests.

// file: test/bootstrap.js

Relay.createContainer = ((create) => {
  let disabled = 0;

  const replacement = (Component, ...args) => {
    const Container = create(Component, ...args);
    const Wrapper = (...args) => {
      if (disabled) { return new Component(...args); }
      else { return new Container(...args); }
    };

    Object.assign(Wrapper, Container);

    // allow context population when possible, but don't validate any props
    Wrapper.contextTypes = _.mapValues(
      Container.contextTypes || {}, _.constant(React.PropTypes.any)
    );

    return Wrapper;
  };

  replacement._real = create;
  replacement.disable = () => { disabled++; };
  replacement.enable = () => { disabled--; };

  return replacement;
})(Relay.createContainer._real || Relay.createContainer);
// file: src/components/application.js

class Application extends React.Component {
  // ...
}

export default Relay.createContainer(Application, {
  fragments: {
    viewer: () => Relay.QL`
      fragment on User {
        id
        name
      }
    `,
  },
});
}
// file: test/components/application.js

import Application from '../../src/components/application';

describe('<Application />', () => {
  before(() => { Relay.createContainer.disable(); });
  after(() => { Relay.createContainer.enable(); });

  it('can be tested', () => {
    const wrapper = shallow(<Application viewer={ { name: 'Grace' } } />);

    expect(wrapper).to.have.descendants('Link');
  });
});

Run with mocha --require test/bootstrap.js or add the flag to test/mocha.opts.

Was this page helpful?
0 / 5 - 0 ratings