Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test decorated React component with shallow rendering

Tags:

I am following this tutorial: http://reactkungfu.com/2015/07/approaches-to-testing-react-components-an-overview/

Trying to learn how "shallow rendering" works.

I have a higher order component:

import React from 'react';

function withMUI(ComposedComponent) {
  return class withMUI {
    render() {
      return <ComposedComponent {...this.props}/>;
    }
  };
}

and a component:

@withMUI
class PlayerProfile extends React.Component {
  render() {
    const { name, avatar } = this.props;
    return (
      <div className="player-profile">
        <div className='profile-name'>{name}</div>
        <div>
          <Avatar src={avatar}/>
        </div>
      </div>
    );
  }
}

and a test:

describe('PlayerProfile component - testing with shallow rendering', () => {
  beforeEach(function() {
   let {TestUtils} = React.addons;

    this.TestUtils = TestUtils;

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<PlayerProfile name='user'
                                            avatar='avatar'/>);
  });

  it('renders an Avatar', function() {
    let result = this.renderer.getRenderOutput();
    console.log(result);
    expect(result.type).to.equal(PlayerProfile);
  });
});

The result variable holds this.renderer.getRenderOutput()

In the tutorial the result.type is tested like:

expect(result.type).toEqual('div');

in my case, if I log the result it is:

LOG: Object{type: function PlayerProfile() {..}, .. }

so I changed my test like:

expect(result.type).toEqual(PlayerProfile)

now it gives me this error:

Assertion Error: expected [Function: PlayerProfile] to equal [Function: withMUI]

So PlayerProfile's type is the higher order function withMUI.

PlayerProfile decorated with withMUI, using shallow rendering, only the PlayerProfile component is rendered and not it's children. So shallow rendering wouldn't work with decorated components I assume.

My question is:

Why in the tutorial result.type is expected to be a div, but in my case isn't.

How can I test a React component decorated with higher order component using shallow rendering?

like image 449
eguneys Avatar asked Jul 16 '15 16:07

eguneys


People also ask

Does React testing library shallow render?

React Testing libraryEnzyme's shallow renderer doesn't render sub-components, so React Testing Library's render method is more similar to Enzyme's mount method. In React Testing Library, you don't need to assign the render result to a variable (i.e. wrapper ).

What does testing with shallow rendering mean?

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.

How do you check if a component is re rendering?

There's a checkbox well hidden in the React DevTools settings that allows you to visually highlight the components that rerendered. To enable it, go to "Profiler" >> click the "Cog wheel" on the right side of the top bar >> "General" tab >> Check the "Highlight updates when components render." checkbox.

Which of the following testing utility does the shallow rendering a useful constraint to test component as a unit?

Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behavior of child components. As of Enzyme v3, the shallow API does call React lifecycle methods such as componentDidMount and componentDidUpdate .


Video Answer


2 Answers

You can't. First let's slightly desugar the decorator:

let PlayerProfile = withMUI(
    class PlayerProfile extends React.Component {
      // ...
    }
);

withMUI returns a different class, so the PlayerProfile class only exists in withMUI's closure.

This is here's a simplified version:

var withMUI = function(arg){ return null };
var PlayerProfile = withMUI({functionIWantToTest: ...});

You pass the value to the function, it doesn't give it back, you don't have the value.

The solution? Hold a reference to it.

// no decorator here
class PlayerProfile extends React.Component {
  // ...
}

Then we can export both the wrapped and unwrapped versions of the component:

// this must be after the class is declared, unfortunately
export default withMUI(PlayerProfile);
export let undecorated = PlayerProfile;

The normal code using this component doesn't change, but your tests will use this:

import {undecorated as PlayerProfile} from '../src/PlayerProfile';

The alternative is to mock the withMUI function to be (x) => x (the identity function). This may cause weird side effects and needs to be done from the testing side, so your tests and source could fall out of sync as decorators are added.

Not using decorators seems like the safe option here.

like image 134
Brigand Avatar answered Sep 18 '22 12:09

Brigand


Use Enzyme to test higher order / decorators with Shallow with a method called dive()

Follow this link, to see how dive works

https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/dive.md

So you can shallow the component with higher order and then dive inside.

In the above example :

const wrapper=shallow(<PlayerProfile name={name} avatar={}/>)
expect(wrapper.find("PlayerProfile").dive().find(".player-profile").length).toBe(1)

Similarly you can access the properties and test it.

like image 40
Kamaraju Avatar answered Sep 18 '22 12:09

Kamaraju