单元测试到底测什么?

转载自:htp://ww.ltesting.net

以国内互联网的开发节奏,在前端业务项目中全面覆盖单元测试有时显得不太可行,主要是因为以下.

这些绊脚石:
●UI交互复杂,路径难以覆盖全面.
●工期紧,开发对实践TDD , BDD所带来的长远效益没有信心
●产品经理们时不时打着「敏捷开发」的旗号改需求,使得刚刚辛辛苦苦写完的测试脚本完全作废
在这样的处境下,一味强调单元测试的逻辑覆 盖率是没有太大意义的,明确在哪里应用单测的能取得最大的边际效益是更有意义的事情。
以下笔者根据自己的- -些在单测的实战经验,列出了三项关于「单元测试应该测什么」的观点并附以一些例子与大家交流:

单元测试并非测试的全部拿来主义地对待单元测试单测只是一-种局部模块测试,是诸多测试方案中的一种,认识到这一点可以避免我们为了测试而测试,或者为了指标而测试。
同时也应该认识到单测本身的覆盖能力也是有限的,全部用例的PASS和100%的覆盖率都不能保证被测试模块的所有逻辑路径都有正确的行为。

是否对一个模块使用单元测试往往取决于这个模块的逻辑稳定性和业务类型
例如对于一一个底层npm包项目,单元测试几乎 是他唯一的代码质 量保障手段,这时就应该尽可能通
过单元测试验证它在各种应用场景下的行为是否符合预期,来最低成本地保证它每次发包和更新的质
量。对这类项目,彻底应用BDD开发模式也会获得越来越高的开发效率收益。
而对于-一个功能复杂的UI组件,除了单元测试,还有E2E测试,自动化回归测试,QA手动测试
( 😊 )来保障它的代码质量。此时使用单元测试的边际效益可能不是最高的,可以考虑通过别的手
段来回归它的逻辑。也可以考虑在初版功能验证上线后通过快照测试( snapshot )来回归验证每-次
迭代的逻辑。
边界环境的模拟
让模块穿梭时空
单测的一个很重要的意义是帮助我们在开发阶段模拟出QA手动测试( 😊 )甚至线上使用场景下
都不易触达的边界场景,如:
●模拟个别浏览器下的JS版本
●模拟某个URL状态.
●模拟某种本地缓存状态
●模拟不同时区下的情形
●模拟时间过了-一个小时(这几乎只有单元测试能够做到)
等等
使用这类模拟对模块进行单元测试的边际效益是极高的,往往比QA去作等价的模拟快得多。
比如下面这段脚本,通过jest的timer mock能力,实现了对expire 函数的测试:

const expire = (callback) => setTimeout(callback, 60000); // -分钟以后过期
test('到点就调用回调’,() => {
const callback = jest. fn();
expire(callback);
jest . advanceTimersByTime(59999);
expect(callback) . not . toBeCalled();
jest . advanceTimersByTime(1);
expect(callback). toBeCalledOnce();

这段代码通过jest. advanceTimersByTime精确模拟了宏任务的运行过程,同步完成了原本需要一分
钟才能验证一次的异步流程的测试。
又比如下面的测试脚本用来测试一个名为 catchFromURL 的工具函数,该函数可以从当前的URL中
获取指定的参数作为返回值返回,同时从URL中抹去该参数。
这中需求通过URL携带token 信息的业务场景(如单点登录)中是非常常见的。

test( '通过URL获取指定的参数值并抹去之'() => {
const CURRENT_ ORIGIN = document . location. origin;
const testHref =、 ${CURRENT_ 0RIGIN}/list/2/detail?a=123b&b=true#section2^ ;
history . replaceState(null,。testHref);
expect(catchFromURL('a' )). toBe('123b');
expect(document . locat ion. href) . toBe(~ ${CURRENT_ ORIGIN}/list/2/detail?b-true#section2^ );

这段测试代码通过jsdom来实现对需要测试的环境的模拟。环境的构造和模拟其实是单元测试中的
一个难点,由于jsdom本身的一些缺陷(如没有实现Navigator )使得在测试脚本运行的node环境中
模拟正确的浏览器环境往往需要用到很多的Hack技术,这一点在未来的夜点心中会着重中展开讨论。
点到为止

less is more

测试代码无需关心被测试模块的具体实现,点到为止地测试几种必要的流程场景即可。这一方面可以
减少写测试逻辑的时间, - -方面可以使得业务逻辑具有更大的实现自由度。
对一个业务模块,测试脚本只需要关心该模块所关联的所有外部性即可:
●对于函数模块而言,控制它引用的模块、它的输入和它的副作用,验证它的输出和对副作用的影响
●对于组件模块而言,控制它依赖的服务、它依赖的子组件、它的props和它的事件,验证它的渲染
结果和props中回调的调用情况,而不应该关心它的state。
下面的脚本通过enzyme 组件测试工县测试了一个名为ValidatableInput 的React组件。这个组件
在失焦( blur )时会触发onValidate 回调,并传入inputValue 参数。

test(‘失焦时触发onvalidate', () => {
const onValidate = jest .mock();
const inputvalue =‘输入的内容;
const wrapper = shallow(
<ValidatableInput
placeholder={' '}
value={inputValue}
alert={''}
onChange={onChange}
onValidate={onValidate}
/>
);
wrapper。
.find(' .validatable input' ).first().simulate("blur');
expect(onvalidate)toBeCalledwith(inputValue);
});

在上述测试用例中我们的测试逻辑完全基于行为开展,只关心失焦的[动作」和执行回调的[反.
馈」,没有去断言任何关于组件状态的内容。
这样组件可以根据它的需要自由地实现它的内部逻辑,例如添加通过外部的Provider 来提
供value 和onChange成为受控组件的能力。这些实现的变化都不会影响当前这条测试用例的有效性。

上面就是一一些对应该用单元测试测什么的看法 ,把单测用在它最擅长的地方,才能在紧凑的开发节奏中取得事半功倍的效果。

微信搜一搜【程序员阿沐】关注这个文绉绉的程序员,关注后回复【面试】有我准备的一线大厂面试资料和简历模板,希望大家都能找到心仪的工作,学习是一条时而郁郁寡欢,时而开怀大笑的路,加油。如果你通过努力成功进入到了心仪的公司,一定不要懈怠放松,职场成长和新技术学习一样,不进则退。如果有幸我们江湖再见!

如果对软件测试、接口、自动化、性能测试、测试开发、面试经验交流。感兴趣可以810119819,群内会有不定期的发放免费的资料链接,这些资料都是从各个技术网站搜集、整理出来的,如果你有好的学习资料可以私聊发我,我会注明出处之后分享给大家。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值