单元测试基础
2019-08-28

为什么要有这篇文章呢?

个人在不断实践中越发觉得,单元测试对于代码质量的保障真的太有意义了,至少能体现在如下两个方面:

①让你写出更好的代码,可测试的代码一定是优雅的代码(为了可测试,你必须要解耦,必须要遵循较好的设计模式)

②让你重构之类的操作更加放心,因为测试会告诉你影响了哪些功能点

所以想简单写写关于单元测试的一些基础相关东西

 

单元测试是什么?

也不想上网找定义,就说说大概自己的几点理解:

单元测试是单元的,即只是对一个系统里某个特定模块(或者方法)有限条件下(比如某个if分支)的测试

单元测试不应该依赖任何外部系统(如网络/数据库甚至是时间)所有依赖都应该通过依赖注入的形式获取

单元测试是可以重放的,一次成功后任意情况重试应该也总是成功的

 

举个栗子

为了说明白单元测试,下面写一个简单的代码

首先他的功能很简单,返回当天是什么时刻(上午/下午之类的),具体需求就是

0到6点返回“晚上” 6到12点返回“上午” 12到18点返回”下午” 18点到24点显示”傍晚”(别纠结谁家傍晚还能到24点这种细节了)

此时我们可能会写出如下代码:

1 public string 当天时刻() 2 { 3 DateTime time = DateTime.Now; 4 if(time.Hour >= 0 && time.Hour < 6) 5 { 6 return "晚上"; 7 } 8 if(time.Hour >= 6 && time.Hour < 12) 9 { 10 return "上午"; 11 } 12 if(time.Hour >= 12 && time.Hour < 18) 13 { 14 return "下午" 15 } 16 return "傍晚"; 17 }

 

上面这段代码,从功能角度来说,是完美的,他完美的实现了需求。

但是从测试的角度来说,他是灾难性的,因为这个代码不可测试

为什么这么说呢?

现在电脑时间是上午9点,我运行这个程序,我预期他返回是“上午”。

好了,测试通过,然后另一个小伙伴可能他电脑时钟设置有问题也同一时刻运行却返回了“傍晚”

或者说我在下午的时候运行他返回了给我“下午”

在这里,这个程序执行的结果不确定,他会受到电脑时钟的影响。

 

为什么这个代码不可以测试呢?

我们来分析下这个代码,最重要的一点就是

DateTime time = DateTime.Now;

这句话是获取电脑当前的时间。

我们需要的功能是,告诉我现在是什么时刻,然后这段代码里杂合了获取时间的这么一个非需求内的功能(违反了单一职责)

另外”获取时间”因为是受到外部条件控制(电脑时钟),而这里明确的直接使用了DateTime.Now,所以也可能违反了依赖倒置原则

 

如何解决呢?

其实并不复杂,我们只要将方法内获取时间改为通过参数的形式传递进来好了

 

1 public string 当天时刻(DateTime time) 2 { 3 if(time.Hour >= 0 && time.Hour < 6) 4 { 5 return "晚上"; 6 } 7 if(time.Hour >= 6 && time.Hour < 12) 8 { 9 return "上午"; 10 } 11 if(time.Hour >= 12 && time.Hour < 18) 12 { 13 return "下午" 14 } 15 return "傍晚"; 16 } 17

 

这样做意味着什么呢?

意味着将时间的获取交给外部去处理,而方法内将只专注于处理“获取当天时刻”相关的主线逻辑,遵循单一职责

 

此时如果我要对这个方法进行测试的话就可以写出如下单元测试用例,且下述用例永远不会受到外部条件影响,只要”当天时刻”这个方法不出bug他永远该是对的就是对的

1 //简化代码,假设当天时刻是当前类里的静态方法,假设使用了Shouldly类库来做Assert 2 [Fact] 3 public void 上午九点_应该为上午() 4 { 5 当天时刻(new DateTime(2018,1,1,9,0,0)).ShouldBe("上午") 6 } 7 8 [Fact] 9 public void 下午三点_应该为下午() 10 { 11 当天时刻(new DateTime(2018,1,1,15,0,0)).ShouldBe("下午") 12 }

有了这个单元测试后,日后是不是想重构“当天时刻”这个方法也多了个保障,因为一旦你改错了,单元测试会诚实告诉你改出问题了

而且通过将代码改为“可测试”的,也将代码的优雅程度提高,使其遵循了单一职责,并且避免了违反依赖倒置原则

 

另外这个故事告诉我们,小手一抖,就能违反n个原则。。。(隔壁家:不就改了个DateTime.Now嘛,怎么就搞出那么多有的没的)