做更好的单元测试:关于单测你必须知道的技巧与原则

做更好的单元测试:关于单测你必须知道的技巧与原则

最近因工作需要不得不对单元测试中的Mockito2和Powermock框架的一些新特性进行研究:比如Mockito2和Powermock可以伪造静态方法、final类甚至是构造函数的调用,但是研究一段后发现,这些功能其实在小编本来就很熟悉的Jmockit框架中就能实现,而且不用像mockito一样需要特殊的语法和额外的样板代码,看似掌握了一些所谓“高大上”的用法,实际对工作来说没有任何收益。因此今天这篇文章不会讲什么单测框架的高级特性,反而,我们来聊一聊指导我们进行单元测试的一些基本准则。

mock还是不mock?

为什么我们需要mock来进行单元测试呢?为什么我们需要用一些假对象来替换我们测试类中的真对象呢?原因是我们想让自己的单元测试是密闭独立的,实际测试时,任何一个类都有可能依赖于其他的类,这些依赖可能来自于同一源代码的同一根目录,可能来自一些核心库(java.util.ArrayList, java.io.File),更有一大部分来自于第三方库。或许这些依赖的输出是稳定且我们可以预期的,但实际生产环境中,它们可能会依赖于文件系统、网络等这些变幻莫测的外部资源,比如任何一个使用当前日期/时间或读取硬件资源的对象,其结果对我们来说都是不可预知的,而这样的不可预知,对于单元测试来说,就是最大的bug。

在单元测试中,我们需要保证除了我们要测试的类,其“外部世界”的行为与输出与我们所预期的完全一致。举个栗子,比如我们需要测试一个service,这个service作用是根据各国的区号,计算当前国家的税率。

double taxRate = TAXService.getTAXRateForCountry(countryCode);

比如在单测case中,我们假设美国的税率是21%,但是除非我们ctrl+鼠标点击进去TAXService这个类中去查看,我们无法得知是否真的是我们所预期的21%。有可能TAXService类依赖于本地文件,也有可能会去服务器中去查,而查询本地文件,或者连接服务器这一动作,就大大降低了我们单元测试的效率。单元测试的目的就是为了快速地测试这段代码的可用性,如果想要确保整体应用于预期一致,我们要做的并非单元测试,而是集成测试、端对端测试、压力测试等。。。

“既然mock这么好,那所有的case都mock好了”

--by 从前的小编

但是,如果是下面的例子呢:

// 非mock的写法
String postCode = employeeDao
     .getEmployeeById(employeeId)
     .getPreviousEmployer()
     .getAddress()
     .getPostCode();
// mock的写法
when(employeeDao.getEmployeeById(42)).thenReturn(employee);
when(employee.getPreviousEmployer()).thenReturn(previousEmployer);
when(previousEmployer.getAddress()).thenReturn(address);
when(address.getPostCode()).thenReturn(“1234AB”);By Jove, we’re finally there!

哪个更简单明了,已经不言而喻了吧。

注释还是不注释?

在编写单元测试代码时,为了使读代码人的人清除这部分的case测试点在哪里,很重要的一部分是编写注释,有效的注释与漂亮的代码同样重要。这里要强调的是“有效“,在一些时候,过多无用的注释对文件和对你本身也是一种累赘。

如果你的代码不言自明,那就别注释了。比如下面这个例子:

// 过滤关键字
for (String word: word) {...}

// 基本税金
int taxmoney = ...;

// 扣除税费减免
finalTax = (taxItems * taxPrice)
            - min(5, taxItems) * itemPrice * 0.1

下面是几个有效注释的例子:

1、解释你的意图:解释代码为什么这么做,而不是做了什么

// 最终税金 = 税金 - 减免金

2、做好todo:,避免其他人“修复”了你的代码

// TODO: 优化税费计算公式,保证小数点后3位

3、做好问题澄清,避免在code review时别人的误解

// 使用这个顺序计算税率的原因是。。。

写不写before?

请看下面这个例子:

private final Tax tax = new Tax();
@(1.4 微信公众号)Before
public void setUp() {
    tax.increment("key1", 8);
    tax.increment("key2",100);
    tax.increment("key1",0);
}

//1000行之后
@Test
public void testIncrement_existingKey()
{
assertEquals(9, tally.get("key1"));
}

除非再把页面拉回去,我们无法得知1000行之后的单测case的结果是否正确。抽象点来说,就是原因与结果相隔太远了

小编比较倾向的单测case的写法,是把原因与结果放在一个case中,像我们说话一样,开始为原因,结束为结果,这样的好处是代码可读性更强,维护更简单,后续同学的使用也更方便。

private final Tax tax = new Tax();
@Test
public void testIncrement_newKey() {
    tax.increment("key", 100);
    assertEquals(100, tax.get("key"));
}

@Test
public void testIncrement_incrementByZeroDoesNothing() {
    tax.increment("key", 8);
    tax.increment("key", 0);
    assertEquals(8, tax.get("key"));
}

结语

其实,做单元测试的意义就在于能使测试人员得到更好地测试覆盖率,使团队得到更高的产出效率,使研发人员更自信地进行重构,如果我们写的单元测试既臃肿又不好维护,那做单测的意义还何在呢?

原文地址:https://www.cnblogs.com/borishou/p/8695488.html

时间: 2024-10-01 19:46:26

做更好的单元测试:关于单测你必须知道的技巧与原则的相关文章

H2数据库做单测数据库时踩到的坑

H2数据库用来做单测数据库,可以自定义初始化数据,不用担心数据库内容更改造成单测跑不过问题,不过H2数据库跟实际使用的Mysql还是有一定区别. 1. H2数据库不支持Mysql的批量更新功能,支持批量插入 --批量更新(H2不支持) <update id="increaseBatch" parameterType="java.util.List"> <foreach collection="list" item="i

【原创】单测代码生成工具Evosuite试用

工具简介 Evosuite是国外大学机构主导开发的一款开源的Java单测代码生成工具,在数次SBST Unit Testing Tool Competition中评测获得最高分.官网地址:http://www.evosuite.org/. Evosuite的主要特性(翻译自官网): 1.对于指定类,生成Junit4类型的单测代码. 2.根据不同覆盖指标调整生成的用例,例如 行覆盖,分支覆盖,输出覆盖等. 3.单测用例最小化,只有对覆盖率有贡献的单测用例才会被保留. 4.生成的单测用例中包含Jun

Jenkins+maven+git+sonar 系统持续集成&amp;代码单测管理

Jenkins+maven+git+sonar 系统持续集成&代码单测管理 Jenkins的安装 Jenkins是基于Java开发的一种持续集成工具,用于监控持续重复的工作,功能包括: 1.持续的软件版本发布/测试项目. 2.监控外部调用执行的工作. 1.下载最新的版本(一个 war 文件).Jenkins官方网址: http://Jenkins-ci.org/ 2.运行 java -jar jenkins.war(jenkins默认使用的是内嵌的jetty服务,用户也可以布置到其它容器中.)

腾讯陈磊讲产品:3-5个人做不好的事,30-50人做更烂

在中国最大的CIO大会,由<商业价值>& ITValue主办的[2014 IT价值峰会]上,腾讯云CEO陈磊做了一个非常“干货”的报告.他浓缩腾讯培训新员工课程的一些内容,详细阐述了“动态运营”等经历和经验.以下是演讲全文,小标题为钛媒体所加: 我在谷歌互联网公司工作的时候,第一次听说了云计算.按照现在的概念,那时候我就在做云.今天我们谈互联网思维,大家想的更多的是思维转变,商业模式如何转变,产品如何转变.通过这些转变真正去改造一个企业,改造一个公司的运作模式,都是从技术开始的.技术是

对私有静态方法进行单测

对私有静态方法进行单测 1.被单测的类: /** * Created by 58 on 2016-8-21. */ public class Student { private static String reading(String bookName) { return "student read " + bookName; } } 2.使用junit方式 @Test public void readingTest() { String expected = "studen

重拾梦想,做更好的自己

亥时,就寝,忽入空灵,甲申年出师已历一纪,诸多记忆电光石火逐一闪现.时年家贫无靠,生计无着,每日波奔却心系梦想,虽日日身疲体倦,却每以<孟子·告子下>篇中名句“天降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行指乱其所为,所以动心忍性,曾益其所不能也”以慰寸心,类比篇中清史名人,胸中满溢浩然正气,行事尽显峥嵘:历12载,生活稳定,已婚并育一女,四老体健而心宽,内子贤而持家,小女伶俐活泼,此三项尽得,可谓得意了. 然忆及往昔践行之路与现时行走之途,高下立判,原所行皆可日日前行,步步

努力做更好的自己

我们必不可停止探索,而一切探索的尽头,就是重回起点,并对起点有首次般的了解. -----艾略特 一:谁动了“老油条”的奶酪. 老油条们虽然有丰富的工作经验,但没有了激情,认真,合作,进取之心,再多的经验也只能发出微弱的荧光,不能照亮他人. 1.1时间是把杀猪刀 有人说,时间是把杀猪刀,他就像一个刽子手把每个人温情的屠宰,最后的结局是被宰的半死而不自知还是摆脱他的魔爪控制,就看我们每个人的觉悟了. 不要迷信经验. 在各个行业都存在一个“30”岁现象,只要不努力提高自己,就很有可能排在沙滩上.不断提

ngVerify - 更高效的 angular 表单验证

ngVerify v1.5.0 a easy Angular Form Validation plugin.简洁高效的__angular表单验证插件__ See how powerful it.看看它有多强大 动态校验 自动关联提交按钮 多种 tip 校验消息提示 不只校验 dom 元素值,还可以校验 ngModel 数据模型 支持任意类型表单元素,甚至可以校验非表单元素 提供 type 类型校验模板,你几乎不需要定校验规则 提供自定义规则 支持第三方组件校验 Show HOME - 首页 DE

【做更好的职场人】理性、弹性、开放的沟通

好一段时间没逛茶馆了. 看到这个随笔, 正合自己口味, 就谈点自己的经历和想法吧. 我想说的是沟通的事情.不是因为我自己的沟通能力有多好,而是因为自己经历了一个沟通由不好变得更好的过程.这其中有来自小伙伴的宽容,也有他们的真知灼见和好的影响. 还记得我刚来公司不久,公司日志改造升级,由V1改成V2.我可不是来记仇的哦.底层API变了.然后上层得改调用方式.因为交易涉及的任务还有点多,而且很多任务承载着很大流量的业务.当时我就跑到日志平台那边,跟他们理论,说这个改造应该是对业务方透明的,这样改会影