trevordmiller trevordmiller - 2 months ago 15
React JSX Question

React Testing: Event handlers in React Shallow Rendering unit tests

Background



React Shallow Rendering tests

I am trying to learn how to use the React Shallow Rendering TestUtil and had the tests passing until I added an
onClick
event handler to both; It seems that there must be some difference with the
Accordion.toggle
function I am trying to use in
Accordion.test.js
vs
this.toggle
in
Accordian.js
...but I can't figure it out.

Question



How can I get the two highlighted tests in
Accordian.test.js
to pass?

Steps to reproduce




  1. Clone https://github.com/trevordmiller/shallow-rendering-testing-playground

  2. npm install

  3. npm run dev
    - see that component is working when you click "Lorem Ipsum"

  4. npm run test:watch
    - see that tests are failing


Answer

There are a number of issues preventing your tests from passing.

Looking at the test "should be inactive by default":

  1. Accordion.toggle in your test is a property of the Accordion class, and this.toggle in your code is a property of a instance of the Accordion class - so in this case you are comparing two different things. To access the 'instance' method in your test you could replace Accordion.toggle with Accordion.prototype.toggle. Which would work if it were not for this.toggle = this.toggle.bind(this); in your constructor. Which leads us to the second point.

  2. When you call .bind() on a function it creates a new function at runtime - so you can't compare it to the original Accordion.prototype.toggle. The only way to work around this is to pull the "bound" function out of the result from render:

    let toggle = result.props.children[0].props.onClick;
    
    assert.deepEqual(result.props.children, [
      <a onClick={toggle}>This is a summary</a>,
      <p style={{display: 'none'}}>This is some details</p>
    ]);
    

As for your second failing test "should become active when clicked":

  1. You try calling result.props.onClick() which does not exist. You meant to call result.props.children[0].props.onClick();

  2. There is a bug in React that requires a global "document" variable to be declared when calling setState with shallow rendering - how to work around this in every circumstance is beyond the scope of this question, but a quick work around to get your tests passing is to add global.document = {}; right before you call the onClick method. In other words where your original test had:

    result.props.onClick();
    

    Should now say:

    global.document = {};
    result.props.children[0].props.onClick();
    

    See the section "Fixing Broken setState()" on this page and this react issue.