如何写单元测试

作者: 这事情急不得 | 来源:发表于2019-04-12 00:02 被阅读22次

二话不说,先上结论王道:

测试代码的优雅和产品代码的优雅,两者一般不可得兼,舍测试代码的优雅而取产品代码的优雅,方为王道。

然后给出全球唯一的参考书籍,因为市面上讲怎么写测试代码的书,应该只有这一本权威。

然而,尽信书不如无书,这本里讲的都是到了万不得已的时候,不得已而为之的手段。如果还有更简单的手段,千万不要用这本里讲的东西,我下文反对的某些手段就是源自本书。

现在,回归正题,首先,为什么要舍测试代码的优雅而取产品代码的优雅?因为一旦出了bug,必然是首先看的产品代码。而测试代码,其帮助理解产品业务逻辑的功效完全无法和产品代码本身相比,所以,产品代码比测试代码更重要。产品代码的可维护性比测试代码的可维护性要重要的多。

现在,问题的重点,为什么测试代码的优雅和产品代码的优雅,两者一般不可得兼?这是因为你写测试的时候很多时候得mock,而mock并不是这么容易的。

比如class A 要用到 function B,你的测试要mock fuction B,然而不巧这个B正好是private function,无法mock。

很多人有这样的想法,当我无法写一个test的时候就说明了我的产品代码的design是有问题的,通过写test能够揭露我的产品代码是不是有design问题。这种想法是错误的,因为可能是你的技艺不精所以导致你写不出test,而不是真的没法写test。

比如说,对于这个例子,很多人想我把这个B变成public不是就可以mock了吗?但是把B变成public貌似就扩大了B的职责这违反了单一职责原则,所以我最好的做法是把B封装在class C里面,然后把C的object传入给A的构造函数。这样来理解单一职责原则是有问题的。

单一职责原则说的是一个class只做一件事,但是这一件事的理解各个人都各有不同,是非常主观的。比如说起床,可以包含穿衣服,洗脸,刷牙等一系列事情,那么你说起床是一件事呢还是几件事的集合呢?更加夸张的说,如果一定要分的很细,那么我可以认为每一行code就是做了一件事了,是不是每一行code都要封装成一个class呢?显然不是的。

问题的关键是,没有搞清楚整个面向对象要解决的根本问题。其实有一条凌驾于所有原则之上的原则,那就是DRY(Do not repeat yourself)原则。所有的其他原则,设计模式等等最终就是要解决代码重复的问题,代码重复问题是所有代码可维护性问题的根本问题。

所以,如果说世界上只有我一个人起床,那么我大可认为起床整个过程,包括洗脸刷牙等都是一件事。但是如果世界上有2个人以上要洗脸,那么洗脸的代码必须抽象出来,这2个人洗脸的时候只要调用同一份代码就可以了。这是follow了 DRY原则。

所以在单一职责中到底怎么看是不是单一的职责,怎么划分职责的大小,就是看这个职责有没有可能有代码重复,划分的依据是有没有代码重复,而不是你主观的认为,不知道到这里各位想通了没有。

回到之前的例子,如果class A 当前只有一个object,那么再弄一个class C去封装function B就是多余的,因为A并没有违反DRY原则,而再弄出来的C也没法复用。当没有办法复用的时候,为什么要去封装呢?本来B在A的内部封装的好好的,现在A要和C通讯,这就违反了高内聚低耦合原则。你看,为了不违反单一职责原则,反而违反了高内聚低耦合原则,这是为什么呢?各位想通了吗?

本来只有A,现在又有了C,这是为了写测试,而把产品代码弄的不优雅了,违背了我们的结论:舍测试代码的优雅而取产品代码的优雅。

不要跟我说以后可能会被复用,以后谁也说不准,我们的design要从当前出发,兼顾扩展性,主次不能颠倒,记住唐纳德的名言:

过早的优化是万恶之源

那么在这种情况下,我到底要怎么写test呢?此时必然是要改一下产品代码了,但是能不能把产品代码修改造成的不优雅程度降到最低呢?

我们可以把B从private改成protected,然后在测试代码里写一个MockA去继承A,然后在MockA里改写B来mock掉B。这样虽然测试代码绕了个圈子,但是不至于在产品代码中引入A的新构造函数和C,只是把private稍微扩大了点范围变成了protected,这样就对产品代码的优雅程度的影响降到了最低。

其实,很多情况下通过测public函数就可以覆盖到private函数了,所以一般不需要去测private函数,但是事情无绝对,经常有时候class并不需要复用,所以private占大头,也只能测private。

还有一种常见的就是面向接口编程。有些人写任何class都喜欢加一个接口。只有在你有预感到不久的将来这个class会有别的派生类的时候,才定义需要接口。否则,接口放在那里毫无意义。再重复一次:

过早的优化是万恶之源

《重构》一书中说:只有出现2次或2次以上相同代码的时候,才考虑重构。有些时候,为了简单方便起见,重复2次也是可以接受的。

运用之妙,存乎一心。妙法自然,各位慢慢体会吧。。。

相关文章

  • 上手unittest读这篇文章就够了

    引言 本文主要介绍了如下内容 单元测试的定义; python中如何写基础的unittest单元测试; 详解unit...

  • 单元测试

    本文将介绍以下内容: iOS开发中添加单元测试的方法。 如何写单元测试用例及用例组。 介绍单元测试的一些基础概念。...

  • React单元测试策略及落地

    单元测试是现代软件开发最基本,也普遍落地不力的实践。市面关于React单元测试的文章,普遍停留在“可以如何写”和介...

  • lumen 单元测试

    如果你无法量化它,就无法改进它。 经过讨论,大家均认为单元测试是有帮助的,接下来就是如何写的问题了。 单元测试的意...

  • iOS单元测试

    前言 不写单元测试的程序员是不合格的,为了让自己成为一名合格的程序员,学习如何写单元测试是很有必要的,这里以Xco...

  • 如何写好单元测试

    本文主要阐述单元测试(UT)的重要性,以及解释一些常见的困惑,以帮助我们写出质量更高的 UT。至于类似 Mocha...

  • 如何写单元测试

    什么是单元测试 单元测试(unittesting),是指对软件中的最小可测试单元进行检查和验证,单元是人为规定的最...

  • 如何写单元测试

    二话不说,先上结论王道: 测试代码的优雅和产品代码的优雅,两者一般不可得兼,舍测试代码的优雅而取产品代码的优雅,方...

  • 深入浅出Android单元测试(三)详解Mockito

    对Android有依赖的单元测试如何写?怎样脱离真机与模拟器?本文将会对Java测试框架mockito做详细介绍。...

  • LocalStack: 本机 Mock AWS 服务利器

    如果代码中需要与 AWS 服务(比如 S3)交互, 如何写单元测试? 0x00 mock API 既然是调用 AW...

网友评论

    本文标题:如何写单元测试

    本文链接:https://www.haomeiwen.com/subject/wcsiiqtx.html