How to mock react-router context

血红的双手。 提交于 2019-12-04 08:11:50

问题


I've got fairly simple react component (Link wrapper which adds 'active' class if route is active):

import React, { PropTypes } from 'react';
import { Link } from 'react-router';

const NavLink = (props, context) => {
  const isActive = context.router.isActive(props.to, true);
  const activeClass = isActive ? 'active' : '';

  return (
    <li className={activeClass}>
      <Link {...props}>{props.children}</Link>
    </li>
  );
}

NavLink.contextTypes = {
  router: PropTypes.object,
};

NavLink.propTypes = {
  children: PropTypes.node,
  to: PropTypes.string,
};

export default NavLink;

How am I supposed to test it? My only attempt was:

import NavLink from '../index';

import expect from 'expect';
import { mount } from 'enzyme';
import React from 'react';

describe('<NavLink />', () => {
  it('should add active class', () => {
    const renderedComponent = mount(<NavLink to="/home" />, { router: { pathname: '/home' } });
    expect(renderedComponent.hasClass('active')).toEqual(true);
  });
});

It doesn't work and returns TypeError: Cannot read property 'isActive' of undefined. It definitely needs some router mocking, but I have no idea how to write it.


回答1:


Thanks @Elon Szopos for your answer but I manage to write something much more simple (following https://github.com/airbnb/enzyme/pull/62):

import NavLink from '../index';

import expect from 'expect';
import { shallow } from 'enzyme';
import React from 'react';

describe('<NavLink />', () => {
  it('should add active class', () => {
    const context = { router: { isActive: (a, b) => true } };
    const renderedComponent = shallow(<NavLink to="/home" />, { context });
    expect(renderedComponent.hasClass('active')).toEqual(true);
  });
});

I have to change mount to shallow in order not to evaluate Link which gives me an error connected with the react-router TypeError: router.createHref is not a function.

I would rather have "real" react-router than just an object but I have no idea how to create it.




回答2:


For react router v4 you can use a <MemoryRouter>. Example with AVA and Enzyme:

import React from 'react';
import PropTypes from 'prop-types';
import test from 'ava';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { MemoryRouter as Router } from 'react-router-dom';

const mountWithRouter = node => mount(<Router>{node}</Router>);

test('submits form directly', t => {
  const onSubmit = sinon.spy();
  const wrapper = mountWithRouter(<LogInForm onSubmit={onSubmit} />);
  const form = wrapper.find('form');
  form.simulate('submit');

  t.true(onSubmit.calledOnce);
});



回答3:


Testing components which rely on the context can be a little tricky. What I did was to write a wrapper that I used in my tests.

You can find the wrapper below:

import React, { PropTypes } from 'react'

export default class WithContext extends React.Component {
  static propTypes = {
    children: PropTypes.any,
    context: PropTypes.object
  }

  validateChildren () {
    if (this.props.children === undefined) {
      throw new Error('No child components were passed into WithContext')
    }
    if (this.props.children.length > 1) {
      throw new Error('You can only pass one child component into WithContext')
    }
  }

  render () {
    class WithContext extends React.Component {
      getChildContext () {
        return this.props.context
      }

      render () {
        return this.props.children
      }
    }

    const context = this.props.context

    WithContext.childContextTypes = {}

    for (let propertyName in context) {
      WithContext.childContextTypes[propertyName] = PropTypes.any
    }

    this.validateChildren()

    return (
      <WithContext context={this.props.context}>
        {this.props.children}
      </WithContext>
    )
  }
}

Here you can see a sample usage:

  <WithContext context={{ location: {pathname: '/Michael/Jackson/lives' }}}>
    <MoonwalkComponent />
  </WithContext>

  <WithContext context={{ router: { isActive: true }}}>
    <YourTestComponent />
  </WithContext>

And it should work as you would expect.




回答4:


You can use https://github.com/pshrmn/react-router-test-context for that exact purpose

"Create a pseudo context object that duplicates React Router's context.router structure. This is useful for shallow unit testing with Enzyme."

After installing it, you will be able to do something like

describe('my test', () => {
  it('renders', () => {
    const context = createRouterContext()
    const wrapper = shallow(<MyComponent />, { context })
  })
})



回答5:


You need to know the difference between mount & shallow . The official documentation explained the Shallow Render :

When writing unit tests for React, shallow rendering can be helpful. Shallow rendering lets you render a component "one level deep" and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered. This does not require a DOM.

Then, without complexity, neither contexts :

   const renderedComponent = shallow(<NavLink to="/home" />);

instead of

   const renderedComponent = mount(<NavLink to="/home" />, { router: { pathname: '/home' } });

It will work! 🎉



来源:https://stackoverflow.com/questions/38121454/how-to-mock-react-router-context

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!