remix-run/react-router

Unit testing React components with `Link` components is impossible

gregeinfrank opened this issue ยท 12 comments

I know there is this TestLocation: #686

But, it's not clear how this really helps if I want to unit test a component within my views. Something like a side nav, that has a bunch of Links, I need to set up routes with the TestLocation if I want to just render the side nav alone to unit test?

Can someone maybe explain how I should be using TestLocation properly if that fixes my issue?

Thanks!

I just figured this out last night and it was a moderately confusing experience... Un momento.

jest.dontMock('../Logs');

var React;
var TestUtils;
var Logs, LogHandler, Logstore;
var Router, TestLocation;
var routes;

describe('Logs', function() {
  beforeEach(function(){
    React = require('react/addons');
    TestUtils = React.addons.TestUtils;

    Logs = require('../Logs');
    LogHandler = Logs.LogHandler;
    LogStore = Logs.LogStore;

    Router = require('react-router');
    // Yes, this is officially ok.
    TestLocation = require('react-router/modules/locations/TestLocation');
    TestLocation.history = ['/logs/all/all/date_desc/1'];
    routes = <Router.Route name="logs" path="/logs/:kind/:filter/:sortBy/:page" handler={LogHandler}/>;
  });

  // Yes, the LogHandler does in fact contain Links.
  describe('Filtering', function() {
    it('should filter log data', function() {
      // Implementation detail for my project.
      LogStore.getLogs = jest.genMockFunction().mockReturnValue([
        {id: 0, kind: 'transfer' },
        {id: 1, kind: 'magicdance' },
        {id: 2, kind: 'safetydance' }
      ]);

      Router.run(routes, TestLocation, function(Handler) {
        var handler = TestUtils.renderIntoDocument(<Handler />);

        // SECRET SAUCE
        // ~~~~~~~~~~~~~
        // So this was the gotcha for me. The handler was not the component I was
        // looking for, but some console.logs showed me the way:
        var component = handler.getRouteComponents()[0];
        expect(TestUtils.scryRenderedDOMComponentsWithClass(component, 'log-entry').length).toBe(1);
      });
    });
  });
});

I keep on finding references to this guide in searching for an example on how to test my page handlers, will this be available in the new docs soon?

@ryanflorence unfortunately the reference does not exist

@ryanflorence - your link broke, because it's no longer on master, here's the tagged version.
https://github.com/rackt/react-router/blob/0.13.x/docs/guides/testing.md

Is there a 1.0 version?

@robcolburn we don't have docs for testing yet for React Router 1.0.0-RC1. If anyone is up for writing those I'll be happy to merge that PR. If not I try to write something when I find some free time.

Added a new issue for that #2149

Just in case, the link mentioned before in comments is now: https://github.com/rackt/react-router/blob/master/docs/guides/Testing.md

lomse commented

In case you are using react-test-renderer, you can do the following as well when testing the Landing component which contains aLink component:

import React from 'react'
import renderer from 'react-test-renderer'
import { MemoryRouter } from 'react-router'
import Landing from './Landing'

test('Landing renders correctly', () => {
	const component = renderer.create(
		<MemoryRouter>
			<Landing />
		</MemoryRouter>
	)
	const tree = component.toJSON()
	expect(tree).toMatchSnapshot()
})

Below is the Landing component

import React from 'react'
import { Link } from 'react-router-dom'

const Landing = () => (
  <div className="landing">
    <h1>Movie Library</h1>
    <input type="text" placeholder="Search..." />
    <Link to="/search">or Browse All</Link>
  </div>
)

export default Landing

But do I need to install the whole react-router library just to test one component which contains a Link component?

This problem is further compounded when using enzyme. Enzyme only lets you do things like setState etc. on the root component you have mounted. This means if you wrap your component that contains a Link inside a router, then the root component is now the router and you cannot test your component with setState or update... as least as far as I can see. Any ideas?

Checkout this project: https://github.com/NullVoxPopuli/react-vs-ember/tree/master/testing/react (specifically that folder)

It demonstrates how to test anything and everything worth testing.

Basically, the solution in two parts:

  1. Don't use enzyme
  2. Integration test your components, don't unit test them