}
return isNull;
}
}
// 测试用例类
public class TextTest {
public void testToNumber() {
Assert.assertEquals(123, new Text(“123”).toInt());
}
public void testToNumber_nullOrEmpty() {
Assert.assertNull(null);
Assert.assertNull(new Text("").toInt());
}
public void testToNumber_containsSpace() {
Assert.assertEquals(123, new Text(" 123").toInt());
Assert.assertEquals(123, new Text(" 123 ").toInt());
Assert.assertEquals(123, new Text("123 ").toInt());
Assert.assertEquals(123, new Text("1 23 ").toInt());
Assert.assertEquals(123, new Text("1 2 3 “).toInt());
Assert.assertEquals(123, new Text(” 1 2 3 ").toInt());
}
public void testToNumber_containsInvalidCharacters() {
Assert.assertNull(new Text(“123a”).toInt());
Assert.assertNull(new Text(“1*23”).toInt());
Assert.assertNull(new Text(“abc”).toInt());
}
public void testToNumber_large() {
Assert.assertEquals(Integer.MAX_VALUE, new Text("" + Integer.MAX_VALUE).toInt());
}
}
// 运行用例类
public class TestCaseRunner {
public static void main(String[] args) {
TextTest test = new TextTest();
test.testToNumber();
test.testToNumber_nullOrEmpty();
test.testToNumber_containsSpace();
test.testToNumber_containsInvalidCharacters();
test.testToNumber_large();
}
}
测试结果如下:
从上面的示例我们可以总结出:写单元测试就是针对代码设计覆盖各种输入、异常、边界条件的测试用例,用将用例翻译成代码的过程。
另外,翻译代码时,可利用单元测试框架(如JUnit、TESTNGINX、Spring Test等) 来简化测试代码的额编写。
写单元测试真的是件耗时的事吗?
代码量多,写的过程繁琐,但并不是很耗时,因为不用考虑太多代码设计上的问题,大部分是cv操作。
对单元测试的代码质量有什么要求吗?
单元测试毕竟不会在生产环境运行,类的测试代码都相对独立,代码质量要求可以放低些,命名不是很规范、代码重复有些重复,也是可以的。
单元测试只要覆盖率高就够了吗?
测试覆盖率是比较容易量化的指标,常常作为单元测试写得好坏的评判标准。有很多现成的工具专门用来做覆盖率统计(如JaCoCo、Cobertura等)。覆盖率的计算方式也有很多种,最简单的语句覆盖、稍微高级点的:条件覆盖、判定覆盖、路径覆盖。
盲目追求高覆盖率是不可取的,更重要的是看测试用例是否覆盖了所有可能的情况,特别是一些边界条件。
过度关注覆盖率会导致开发人员为了提高覆盖率,写了很多没必要的测试代码(如get、set)。从过往经验来讲,一个项目的单元测试覆盖率在60~70%即可上线,当然如果项目对代码质量要求严格,亦可适当提高覆盖率要求。
写单元测试需要了解代码的实现逻辑吗?
不需要,单元测试只关心被测函数实现了什么功能,切不可为了追求覆盖率,逐行阅读代码,然后针对实现逻辑编写单元测试。
如何选择单元测试框架?
团队内部统一即可,自己写的代码用已选定的单元测试框架无法测试,多半是代码写的不够好,可测试性差,这个时候要重构自己的代码,使其更易测试,而不是找另一个更加高级的单元测试框架。
单元测试为何难落地执行?
代码的可测试性,粗略地讲就是:针对代码编写单元测试的难易程度,对于一段代码很难为其编写单元测试,或者写起来很费劲,需要依赖单元测试框架中很高级的特性,那么往往意味着代码设计不够合理,代码的可测试性不高。
如果代码中依赖了外部系统或不可控组件(如数据库、网络通信、文件系统等),就需要将被测代码与外部系统解依赖,这种解依赖的方法称作 “Mock”,即用一个 “假” 的服务替代真正的服务,Mock服务在我们的控制下,模拟输出我们想要的数据。Mock方式又分两种,手动和使用框架Mock。
常见的测试不友好的代码有这几种:
“高内聚、低耦合” 的特性可以让我们聚焦在某一模块或类中,而不需要了解太多其他模块或类的代码,让焦点不过于分散,降低阅读和修改代码的难度。依赖关系简单,耦合小,修改代码也不至于牵一发而动全身,代码改动集中,引入bug的风险也少了,而且可测试性更佳,容易Mock或只需Mock少量外部依赖。
除了看改代码会不会牵一发动全身外,还可以把 模块间、类与类间的依赖关系画出来,根据依赖关系图的复杂性来判断是否需要解耦重构。如果依赖关系复杂混乱,那从代码结构上讲,可读性和可维护性肯定不是太好。
准确达意
为目标,在能达意的情况下越短越好,默认或大家都熟知的词,推荐用缩写;注释到底该写什么?(做什么、为什么、怎么做),代码示例如下:
/**
尽管做什么、怎么做可以从代码中体现出来,但是还是建议在注释中写明,原因如下:
当然,不是注释写得越多就越好,太多意味着代码写得不够刻度,而且会对代码本身的阅读造成干扰,后面维护成本也较高。
一般来说:类和函数一定要写注释,且尽可能全面、详细,而函数内部的注释要相对少一些,一般都是靠好的明明、提炼函数、解释性变量、总结性注释来提高代码的可读性。
很难给出一个确切的值,一个间接的判断标准:当一个类代码读起来让你感觉头大、找个函数找半天、用一个小功能就要引入整个类(类中包含很多无关此功能的实现函数)时,就说明类的函数过多了。
一行代码最长不能超过IDE显示的宽度,滚动鼠标才能查看一行的完整代码,显然不利于代码的阅读。
对于较长、又不方便抽取成小函数的函数,除了用总结性注释分隔外,还可以用空行来分割代码块。除此之外,类成员与函数间、静态成员变量和普通成员变量间、各函数间等,都可以通过添加空行的方式让其界限更加明确。
都可以,但项目内要统一,建议不要使用Tab缩进,因为不同IDE的Tab缩进显示宽度不同,要用也行,要统一配置好!
都可以,推荐括号与语句同一行,这样可以节省代码行数,另起一行也行,左右括号垂直对齐,可以方便的看到哪些代码属于哪个代码块,各有千秋,还是那句话项目统一。
建议:类所属包名 → import引入的依赖类(字母序从小到大排列) → 成员变量 → 函数,变量和函数间都是按照先静态后普通,作用域范围从大到小排列。实际上,还有种排列习惯,就是将有调用关系的函数放到一块。
当代码逻辑较复杂时,才建议提炼类或函数,如果提炼出的函数只包含两三行代码,阅读代码时还得跳过去看,反而增加了阅读成本。
参数大于等于5个时,会影响到代码的可行,用起来也不方便,先考虑是否职责单一拆解,或者将函数的参数封装成对象传递。
不要在函数中使用boolean类型的标识来控制内部逻辑,ture走这块逻辑,false走另一块,这违背了单一职责和接口隔离原则,建议拆成两个函数。还有一种判断参数是否为null控制逻辑的情况,同样建议拆解。
函数设置将更要满足单一职责原则,能多单一就多单一。
过深的嵌套本身理解起来比较费劲,嵌套超两层就要思考是否能减少嵌套,几种解决思路:
去掉多余的if或 else语句
、使用编程语言提供的continue、break、return关键字提前退出嵌套
、调整执行顺序
、将部分逻辑封装成函数调用
,多态
等。
常量取代魔法数字,使用解释性变量来解释复杂表达式,示例如下:
过深的嵌套本身理解起来比较费劲,嵌套超两层就要思考是否能减少嵌套,几种解决思路:
去掉多余的if或 else语句
、使用编程语言提供的continue、break、return关键字提前退出嵌套
、调整执行顺序
、将部分逻辑封装成函数调用
,多态
等。
常量取代魔法数字,使用解释性变量来解释复杂表达式,示例如下: