I have to write unit test cases for a PlayerList
container and Player
component. Writing test cases for branches and props is OK, but how do I test the component's methods and the logic inside them. My code coverage is incomplete because the methods are not tested.
Scenario:
Parent component passes a reference to its method onSelect
as a callback to child component. The method is defined in PlayerList
component, but Player
is generating the onClick event that calls it.
Parent Component/Container:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {selectTab} from '../actions/index';
import Player from './Player';
class PlayerList extends Component {
constructor(props){
super(props);
}
onSelect(i) {
if (!i) {
this.props.selectPlayer(1);
}
else {
this.props.selectPlayer(i);
}
}
createListItems(){
return this.props.playerList.map((item, i)=>{
return (
<Player key={i} tab={item} onSelect={() => this.onSelect(item.id)} />
)
});
}
render() {
return(
<div className="col-md-12">
<ul className="nav nav-tabs">
{this.createListItems()}
</ul>
</div>
)
}
}
function mapStateToProps(state){
return {
playerList: state.playerList
}
}
function matchDispatchToProps(dispatch){
return bindActionCreators({selectPlayer: selectPlayer}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(PlayerList);
Child Component:
import React, { Component } from 'react';
class Player extends Component {
constructor(props){
super(props);
}
render() {
return(
<li className={this.props.player.selected?'active':''}>
<a href="#" onClick={() => this.props.onSelect(this.props.player.id)}>
<img src={this.props.player.imgUrl} className="thumbnail"/>
{this.props.player.name}
</a>
</li>
)
}
}
export default Player;
Use enzyme's
.instance()
method to access component methodsThere are a couple of prerequisites of course.
shallow
ormount
functions depending on whether you need to [and/or how you prefer to] simulate events on nested children. This also gives you an enzyme wrapper from which you'll access the component instance and its methods..update
to get a version of the wrapper with spies on which you can assert.Example:
Notes:
PlayerList
andPlayer
, I discovered that you are not assigning a prop calledplayer
toPlayer
; instead you are assigningitem={ item }
. To get this working locally, I changed it to<Player player={ item } … />
.onSelect
you are checking whether the receivedi
argument is falsey and then callingselectPlayer(1)
. I didn't include a test case for this in the above example because the logic concerns me for a couple of reasons:i
could ever be0
? If so, it will always evaluate as falsey and get passed into that block.Player
callsonSelect(this.props.player.id)
, I wonder ifthis.props.player.id
would ever beundefined
? If so, I wonder why you would have an item inprops.playerList
with noid
property.But if you wanted to test that logic as it is now, it would look something like this…
Example testing logic in
onSelect
:You can use enzyme's
simulate
function to test callbacks. You can provide callback functions as spy functions from sinon and verify that it has been called expected times with expected arguments. You can read more on this here: https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/simulate.mdHere is the unit tests for Player and PlayerList components simulating the callback functions. You need the separate PlayerList component as PlayerList and PlayerListContainer(indicating that the component connected to redux). After doing this, you can easily test your PlayerList component.
PlayerListTest.jsx:
PlayerTest.jsx: