在 JEST + ENZYME 中无法达到 100% 的分支覆盖率
Posted
技术标签:
【中文标题】在 JEST + ENZYME 中无法达到 100% 的分支覆盖率【英文标题】:Unable to reach 100% branch coverage in JEST + ENZYME 【发布时间】:2018-01-15 07:28:03 【问题描述】:我的 LoadingIndicator 测试文件中有以下代码。我正在使用 JEST + Enzyme 来测试 LoadingIndicator 容器类。
我一直在努力将分支覆盖率提高到 100%。它卡在 91% 上。我无法涵盖的分支是 LoadingIndicator 容器类中的 if (!this.timeoutID)。请帮助我了解我在这里缺少什么。
import ...;
import ConnectedLoadingIndicator, LoadingIndicator from './loadingIndicator';
jest.useFakeTimers();
describe('LoadingIndicator unconnected container component', () =>
let wrapper;
let instance;
beforeEach(() =>
wrapper = shallow(<LoadingIndicator
loading='true'
error='false' />);
instance = wrapper.instance();
);
it('checks loadingIndicator is hidden on loading false', () =>
wrapper.setProps( loading: false, error: false );
expect(instance.state.showIndicator).toBe(false);
expect(wrapper.find('.loading-hidden').length).toEqual(1);
);
it('checks loadingIndicator is shown after timer runs', () =>
wrapper.setProps( loading: true, error: false );
jest.runOnlyPendingTimers();
expect(instance.state.showIndicator).toBe(true);
expect(wrapper.find('.loading-show').length).toEqual(1);
);
it('checks loadingIndicator is hidden on error true', () =>
wrapper.setProps( loading: true, error: true );
expect(instance.state.showIndicator).toBe(false);
expect(wrapper.find('.loading-hidden').length).toEqual(1);
);
it('checks destroyTimer behavior on loading false and error false', () =>
wrapper.setProps( loading: false, error: false );
expect(instance.timeoutID).toBe(null);
expect(instance.state.showIndicator).toBe(false);
);
);
LoadingIndicator 容器类
import ...;
import LoadingIndicatorComponent from '../../../../components/loadingIndicator';
export class LoadingIndicator extends Component
constructor(props)
super(props);
this.timeoutID = null;
this.state =
showIndicator: false,
;
componentDidMount()
this.ensureTimer(this.props);
componentWillUnmount()
this.destroyTimer();
componentWillReceiveProps(props)
if (props.loading !== this.props.loading
|| props.error !== this.props.error)
this.ensureTimer(props);
ensureTimer(props)
if (props.loading && !props.error)
if (!this.timeoutID)
this.timeoutID = setTimeout(() =>
this.timeoutID = null;
this.setState( showIndicator: true );
, props.timeoutPeriod);
else
this.destroyTimer();
destroyTimer()
clearTimeout(this.timeoutID);
this.timeoutID = null;
this.setState( showIndicator: false );
render()
console.log(this.state.showIndicator);
return (
<div className =
`$this.state.showIndicator ? 'loading-show' : 'loading-hidden'`>
<LoadingIndicatorComponent>
Loading...
</LoadingIndicatorComponent>
</div>
);
const mapStateToProps = (state) => (
loading: isLoading(state),
error: hasError(state),
);
// timeoutPeriod of 1000 is for showing loading indicator after 1000ms
LoadingIndicator.defaultProps =
timeoutPeriod: 1000,
;
export default connect(mapStateToProps)(LoadingIndicator);
【问题讨论】:
【参考方案1】:我已经更新了我的测试文件,如下所示。我发现两个分支都必须在一个测试中覆盖,而不是在单独的测试中。如果已经设置解决了覆盖问题,则检查 timeoutID 保持不变。
import ...;
import ConnectedLoadingIndicator, LoadingIndicator from './loadingIndicator';
jest.useFakeTimers();
describe('LoadingIndicator unconnected container component', () =>
let wrapper;
let spy;
let spyEnsureTimer;
let instance;
let props =
pendingRequest: 0
beforeEach(() =>
wrapper = shallow(<LoadingIndicator ...props />,
lifecycleExperimental: true, );
instance = wrapper.instance();
spy = jest.spyOn(instance, 'componentWillReceiveProps');
spyEnsureTimer = jest.spyOn(instance, 'ensureTimer');
);
it('renders', () =>
expect(wrapper.length).toEqual(1);
);
it('checks for timeoutID remains unchanged if already set', () =>
instance.ensureTimer(props);
expect(instance.timeoutID).toBe(0);
instance.ensureTimer( pendingRequest: 1 );
expect(instance.timeoutID).toBe(1);
instance.ensureTimer( pendingRequest: 1 );
expect(instance.timeoutID).toBe(1);
);
it('checks componentWillReceiveProps and ensureTimer is called', () =>
expect(spy).not.toHaveBeenCalled();
wrapper.setProps( pendingRequest: 1 );
expect(spy).toHaveBeenCalled();
expect(spyEnsureTimer.mock.calls.length).toBe(1);
);
it('checks ensureTimer is not called with same props', () =>
expect(spyEnsureTimer.mock.calls.length).toBe(0);
wrapper.setProps( pendingRequest: 0 );
expect(spyEnsureTimer.mock.calls.length).toBe(0);
);
it('checks loadingIndicator is shown on pendingRequest more than 0', () =>
wrapper.setProps( pendingRequest: 1 );
jest.runOnlyPendingTimers();
expect(instance.state.showIndicator).toBe(true);
expect(wrapper.find('.loading-show').length).toEqual(1);
);
it('checks destroyTimer behavior on pendingRequest less than 1', () =>
wrapper.setProps( pendingRequest: 1 );
jest.runOnlyPendingTimers();
expect(instance.state.showIndicator).toBe(true);
wrapper.setProps( pendingRequest: 0 );
expect(instance.timeoutID).toBe(0);
expect(instance.state.showIndicator).toBe(false);
expect(wrapper.find('.loading-hidden').length).toEqual(1);
);
);
我已经修改了 LoadingIndicator 容器类以及以下内容:
import React, Component from 'react';
import connect from 'react-redux';
import selectPendingRequest from './selectors';
import LoadingIndicatorComponent from '../../../../components/loadingIndicator';
import './loadingIndicator.css';
export class LoadingIndicator extends Component
constructor(props)
super(props);
this.timeoutID = 0;
this.state =
showIndicator: false,
;
componentDidMount()
this.ensureTimer(this.props);
componentWillUnmount()
this.destroyTimer();
componentWillReceiveProps(props)
if (props.pendingRequest !== this.props.pendingRequest)
this.ensureTimer(props);
ensureTimer(props)
if (props.pendingRequest > 0)
if (this.timeoutID === 0)
this.timeoutID = setTimeout(() =>
this.timeoutID = 0;
this.setState( showIndicator: true );
, props.timeoutPeriod);
else
this.destroyTimer();
destroyTimer()
clearTimeout(this.timeoutID);
this.timeoutID = 0;
this.setState( showIndicator: false );
render()
return (
<div className =
`$this.state.showIndicator ? 'loading-show' : 'loading-hidden'`>
<LoadingIndicatorComponent>
Loading...
</LoadingIndicatorComponent>
</div>
);
const mapStateToProps = (state) => (
pendingRequest: selectPendingRequest(state) //count of pendingRequest
);
LoadingIndicator.defaultProps =
timeoutPeriod: 1000,
;
export default connect(mapStateToProps)(LoadingIndicator);
【讨论】:
以上是关于在 JEST + ENZYME 中无法达到 100% 的分支覆盖率的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Jest - Enzyme 测试 React 中 mapStateToProps 中的方法
如何使用 Jest/Enzyme 在功能性 React 组件中测试 lambda 函数?
TypeError:无法读取未定义的属性“viewManagersNames” - 将 Jest 和 Enzyme 与 React Native TypeScript 包集成
React/Jest/Enzyme - 在 .toHaveBeenCalledWith(event) 检查中监视 changeHandler 失败