Testing state change with React, reactive router, joke and enzyme

I am trying to verify with a test that the state of the state with the set state is appropriately changed in componentDidMount

but hit the wall due to react-router.

I am using Enzyme, so I am using mount

lifecycle methods like componentDidMount

. This usually works fine ...

it("changes state after mount", () => {
  const newValue = "new value";

  const testPropertyRetriever = () => newValue;

  const wrapper = mount(
      <StatefulPage
        myProperty="initial value"
        propertyRetriever={testPropertyRetriever}
      />
  );

  // componentDidMount should have executed and changed the state myProperty value
  //     from "initial value" to "new value"
  expect(wrapper.instance().state.myProperty).toEqual(newValue);
});

      

... but this component is problematic because it mount

makes a couple of children deep, in which case one of those children is using react-router <Link>

. Thus, when executing the above test results, errors occur: TypeError: Cannot read property 'history' of undefined

andFailed context type: The context `router` is marked as required in `Link`, but its value is `undefined`.

The answer-router docs recommend that you surround test rendering of a component that requires context (for example, uses react-router <Link>

) with <MemoryRouter>

or <StaticRouter>

, but that won't work because that makes the component under test a child and not the root of the ReactWrapper, which makes it impossible (as far as I know ) getting the state of the component under test. (Given the example above ...

// ...
const wrapper = mount(
  <MemoryRouter>
    <StatefulPage
      myProperty="initial value"
      propertyRetriever={testPropertyRetriever}
    />
  </MemoryRouter>
);

expect(wrapper.childAt(0).instance().state.myProperty).toEqual(newValue);

      

... the test failed ReactWrapper::instance() can only be called on the root

).

I soon learned that the enzyme mount

takes an options argument, which allows the context to be passed to the render, which is what the router needs to react to. So I tried to remove the router wrapper and provide context (based on this answer ) ...

//...
const wrapper = mount(
  <StatefulPage
    myProperty="initial value"
    propertyRetriever={testPropertyRetriever}
  />,
  { router: { isActive: true } }
);

expect(wrapper.instance().state.myProperty).toEqual(newValue);

      

... but this leads to the same errors regarding the type of context I started with. Either I am not passing the context correctly, I don’t know how to get the context to carry over to the descendant it needs, or there’s no way (with these tools) to do it.

Then I went into detail about how I can drown out the context or make fun of one of the components, but have not managed to piece the puzzle together efficiently enough to successfully write and run this test.

How can I check the state of a component as changed to componentDidMount

when it has descendants that depend on a context that satisfies react-router modules?

+3


source to share


1 answer


The router definition provided by the mount function was incomplete.



const MountOptions = {
    context: {
        router: {
            history: {
                createHref: (a, b) => {
                },
                push: () => {
                },
                replace: () => {
                }
            }
        }
    }, childContextTypes: {
        router: PropTypes.object
    }
};
const wrapper = mount(
    <StatefulPage
        myProperty="initial value"
        propertyRetriever={testPropertyRetriever}
    />,
    MountOptions
);

      

+5


source







All Articles