Here we want to test a toggle button component, when the button was click, state should change, style should change also.
Toggle component:
// see this live: https://codesandbox.io/s/GvWpGjKQ import React, {Component} from 'react' import PropTypes from 'prop-types' import glamorous from 'glamorous' import {darken} from 'polished' // imagine this is in a "components" file const primaryColor = '#337ab7' const toggledOnStyles = { backgroundColor: darken(0.15, primaryColor), borderColor: darken(0.25, primaryColor), '&:hover,&:active,&:focus': { backgroundColor: darken(0.2, primaryColor), borderColor: darken(0.3, primaryColor), }, } const toggledOffStyles = { backgroundColor: primaryColor, borderColor: darken(0.1, primaryColor), '&:hover,&:active,&:focus': { backgroundColor: darken(0.1, primaryColor), borderColor: darken(0.2, primaryColor), }, } const ToggleButton = glamorous.button( { display: 'inline-block', padding: '6px 12px', marginBottom: '0', fontSize: '14px', fontWeight: '400', lineHeight: '1.4', textAlign: 'center', cursor: 'pointer', borderRadius: '4px', color: '#fff', }, props => (props.on ? toggledOnStyles : toggledOffStyles), ) class Toggle extends Component { constructor(props, ...rest) { super(props, ...rest) this.state = { toggledOn: props.initialToggledOn || false, } } handleToggleClick = () => { const toggledOn = !this.state.toggledOn this.props.onToggle(toggledOn) this.setState({toggledOn}) } render() { const {children} = this.props const {toggledOn} = this.state return ( <ToggleButton on={toggledOn} onClick={this.handleToggleClick} data-test="button" > {children} </ToggleButton> ) } } Toggle.propTypes = { initialToggledOn: PropTypes.bool, onToggle: PropTypes.func.isRequired, children: PropTypes.any.isRequired, } export default Toggle
Test:
import React from 'react' import {render, mount} from 'enzyme' import Toggle from '../toggle' test('component render with default state', () => { const wrapper = renderToggle(); expect(wrapper).toMatchSnapshotWithGlamor(); }) test('when button is clicked, the style of button should change', () => { const onToggle = jest.fn() // jest mock function const wrapper = mountToggle({ onToggle }) // It is recommended that for the element we need to test // we can add 'data-test' attr, so that we can reference // the element inside testing const button = wrapper.find('[data-test="button"]') // we can verify the style changes inside snapshots expect(wrapper).toMatchSnapshotWithGlamor('1. Before toggle') button.simulate('click') expect(wrapper).toMatchSnapshotWithGlamor('2. After toggle') }) test('onToggle function should be called when the button is clicked', () => { const onToggle = jest.fn() // jest mock function const wrapper = mountToggle({ onToggle }) // It is recommended that for the element we need to test // we can add 'data-test' attr, so that we can reference // the element inside testing const button = wrapper.find('[data-test="button"]') button.simulate('click') expect(onToggle).toHaveBeenCalledTimes(1) expect(onToggle).toHaveBeenCalledWith(true) }) /** * The difference between mount and render function is that * 1. render is faster, because after rendered, it output string, * so there is no lifecycle hooks bind with it. * 2. mount, on the other hand, will bind lifecycle hooks and events, * the output is actual DOM element * */ function mountToggle(props = {}) { const propToUse = Object.assign( {}, { onToggle() { }, children: 'I am a child' }, props ) return mount(<Toggle {...propToUse} />) } function renderToggle(props = {}) { const propToUse = Object.assign( {}, { onToggle() { }, children: 'I am a child' }, props ) return render(<Toggle {...propToUse} />) }