Reputation: 609
I'm trying to learn test-driven development, so I have created a simple training app composed of 2 simple working components App.js which saves an array of data coming from a dummy local JSON file to the App.js component state then map through it and render a User component with props for each element of the data array.
So I have 3 uncovered lines which all contain "if statements" and, I want to achieve 100% test coverage on them, so please help.
Here are the results of the tests.
these are the uncovered lines for App.js from line 18 and 32 are the ternary expressions in each function
clickFollowHandler(id) {
this.setState(prevState => {
const updatedUsers = prevState.users.map(user => {
if (user.id === id) {
user.isFollowed === 'active' ? user.isFollowed = 'idle' : user.isFollowed = 'active'
}
return user
})
return {
users: updatedUsers
}
})
}
clickStarHandler(id) {
this.setState(prevState => {
const updatedUsers = prevState.users.map(user => {
if (user.id === id) {
user.isStared === 'active' ? user.isStared = 'idle' : user.isStared = 'active'
}
return user
})
return {
users: updatedUsers
}
})
}
and this is User.js line 23 is the checkbox ternary expression
return(
<div className={classes.container} key={id}>
<img className={classes.imageContainer} src={myImage} alt={name} />
<div className={classes.contentContainer}>
<div className={classes.contentContainerRow1}>
<div className={classes.name}>name: {name}</div>
<button onClick={() => handleFollowClick(id)}>
{isFollowed === 'active' ? 'Unfollow' : 'Follow'}
</button>
</div>
<div className={classes.contentContainerRow2}>
<div className={classes.date}>date: {date}</div>
<div className={classes.time}>reading time: {readingTime}</div>
<input
className={classes.hvrIconPop}
checked={isStared === 'active' ? true : false}
onChange={() => handleStarClick(id)}
type='checkbox'
/>
</div>
</div>
</div>
)
Now this is my App.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import {shallow, mount} from './enzyme';
import App from './App';
jest.mock('./data/users-data.json')
let {user} = require('./data/users-data.json')
describe('App Component', () => {
it('calling the clickFollowHandler method from App Component has the expected effect on the state of the first user', () => {
const AppComponent = shallow(<App />)
const wrapper = AppComponent.instance()
wrapper.clickFollowHandler('5d552d0058f193f2795fc814')
expect(wrapper.state.users[0].isFollowed).toMatch('idle')
})
})
describe('App Component', () => {
it('calling the clickStarHandler method from App Component has the expected effect on the state of the second user', () => {
const AppComponent = shallow(<App />)
const wrapper = AppComponent.instance()
wrapper.clickStarHandler('5d552d00b20b141dff10d2a2')
expect(wrapper.state.users[1].isStared).toEqual('idle')
})
})
and my User.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import {shallow, mount} from '../../enzyme';
import User from './User';
const users = {
"id": "5d552d0058f193f2795fc814",
"isFollowed": "active",
"isStared": "idle",
"image": "./assets/images/avata.png",
"readingTime": 20,
"name": "Walton Morton",
"date": "Aug 9"
};
it('renders correctly when there are no users', () => {
const tree = renderer.create(<User
key={''}
id={''}
name={''}
date={''}
readingTime={''}
isStared={''}
isFollowed={''}
image={''}
handleFollowClick={() => {}}
handleStarClick={() => {}}
/>).toJSON();
expect(tree).toMatchSnapshot();
});
it('renders correctly when there is one user', () => {
const tree = renderer.create(<User
key={users.id}
id={users.id}
name={users.name}
date={users.date}
readingTime={users.readingTime}
isStared={users.isStared}
isFollowed={users.isFollowed}
image={users.image}
handleFollowClick={() => 'test'}
handleStarClick={() => {}}
/>).toJSON();
expect(tree).toMatchSnapshot();
});
it('when the follow button is clicked a callback is executed', () => {
const mockFollowClick = jest.fn();
const mockStarClick = jest.fn();
const tree = renderer.create(<User
key={users.id}
id={users.id}
name={users.name}
date={users.date}
readingTime={users.readingTime}
isStared={users.isStared}
isFollowed={users.isFollowed}
image={users.image}
handleFollowClick={mockFollowClick}
handleStarClick={mockStarClick}
/>)
const button = tree.root.findByType('button');
const input = tree.root.findByType('input');
button.props.onClick();
expect(mockFollowClick).toHaveBeenCalled();
button.props.onClick();
expect(mockFollowClick).toHaveBeenCalledWith('5d552d0058f193f2795fc814');
input.props.onChange();
expect(mockStarClick).toHaveBeenCalled();
})
describe('User Component', () => {
it('clicking on the button will trigger the click handler', () => {
const mockFollowHandler = jest.fn();
const mockStarHandler = jest.fn();
const wrapper = mount(<User
key={users.id}
id={users.id}
name={users.name}
date={users.date}
readingTime={users.readingTime}
isStared={users.isStared}
isFollowed={users.isFollowed}
image={users.image}
handleFollowClick={mockFollowHandler}
handleStarClick={mockStarHandler}
/>)
wrapper.find('button').simulate('click');
expect(mockFollowHandler).toHaveBeenCalledWith('5d552d0058f193f2795fc814')
})
it('changing the star checkbox will trigger an onChange handler', () => {
const mockFollowHandler = jest.fn();
const mockStarHandler = jest.fn();
const wrapper = mount(<User
key={users.id}
id={users.id}
name={users.name}
date={users.date}
readingTime={users.readingTime}
isStared={users.isStared}
isFollowed={users.isFollowed}
image={users.image}
handleFollowClick={mockFollowHandler}
handleStarClick={mockStarHandler}
/>)
wrapper.find('input').simulate('change');
expect(mockStarHandler).toHaveBeenCalledWith('5d552d0058f193f2795fc814');
})
});
Upvotes: 3
Views: 4589
Reputation: 19772
You need to test against both conditions of the ternary expression (not just one). Since I can't see the complete code, I'll try to offer some brief notes:
For clickStarHandler
and clickFollowHandler
, you'll need to test against users
state and manually invoke the class fields (as you've done). However, you'll need to satisfy the condition when the isFollowed
/isStarred
matches active
and when it matches idle
. Fairly straight forward process, however, if you need to update the users
state to include some data, then you can simply use wrapper.setState({ users: userData });
.
For testing the App
:
import React from 'react";
import { mount } from "enzyme";
import App from "../App";
// for simplicity, I'd recommend only using one user
const usersData = [
{
id: "5d552d0058f193f2795fc814",
isFollowed: "active",
isStarred: "idle",
image: "./assets/images/avatar.png",
readingTime: 20,
name: "Walton Morton",
date: "Aug 9"
}
];
// *optional* include any props that are needed for the App
const initialProps = {};
describe("App Component", () => {
let wrapper;
beforeEach(() => (
wrapper = mount(<App { ...initialProps }/>);
wrapper.setState({ users: usersData }); // not needed if user data is already defined in state
)}
it("sets the 'user.isStarred' state to 'active' or 'idle'", () => (
const invokeStarHandler = () => {
wrapper.instance().clickStarHandler("5d552d0058f193f2795fc814");
wrapper.update();
};
invokeStarHandler();
expect(wrapper.state("users[0].isStarred").toEqual("active");
invokeStarHandler();
expect(wrapper.state("users[0].isStarred").toEqual("idle");
});
it("sets the 'user.isFollowed' state to 'active' or 'idle'", () => (
const invokeFollowHandler = () => {
wrapper.instance().clickFollowHandler("5d552d0058f193f2795fc814");
wrapper.update();
};
invokeFollowHandler();
expect(wrapper.state("users[0].isFollowed").toEqual("idle");
invokeFollowHandler();
expect(wrapper.state("users[0].isFollowed").toEqual("active");
});
...etc.
});
For Users
testing, simply manipulate the user props; for example, changing wrapper.setProps({ isStarred: "active" })
or wrapper.setProps({ isStarred: "idle" })
, then searching for the input
and testing against its props:
import React from 'react";
import { mount } from "enzyme";
import Users from "../Users";
// include any props that are needed for the Users component
const initialProps = {
id: "5d552d0058f193f2795fc814",
isFollowed: "active",
isStarred: "idle",
image: "./assets/images/avatar.png",
readingTime: 20,
name: "Walton Morton",
date: "Aug 9"
}
describe("Users Component", () => {
let wrapper;
beforeEach(() => (
wrapper = mount(<Users { ...initialProps }/>);
)}
it("updates the input's 'checked' property based upon a 'isStarred' prop", () => (
expect(wrapper.find("input").props().checked).toBeFalsy();
wrapper.setProps({ isStarred: "active" });
expect(wrapper.find("input").props().checked).toBeTruthy();
});
...etc
});
On a side note, you can use object destructuring and the spread syntax to vastly simply your React component code.
Upvotes: 4