第1篇: 讲述了如何创造"缝".  "缝"(seam)是需要知道的概念.

第2篇, 避免在构建对象时写出不易测试的代码.

第3篇, 依赖项和迪米特法则.

本文是第4篇, 将介绍全局状态引起的问题.

全局状态

全局状态, 也可以叫做应用程序状态, 它是一组变量, 这些变量维护着应用程序的高级状态.

在程序里, 全局状态可能都存放在一个全局状态对象里, 例如ASP.NET里面的HttpContext; 或者它们可能是全局的变量, 这些全局变量在程序的任何地方都可以访问.

不管是如何实现的全局状态, 每个全局状态变量在内存里只有一个实例. 所以如果一个类里更新了全局变量的值, 那么另一个类访问该变量的时候它的值就是刚才被更新的值.

有些情况下, 使用全局状态确实有用; 但是如果使用不当, 则会对测试造成很大的影响.

全局状态对测试引起的问题

  • 使用静态方法或全局变量访问全局状态的时候, 就引起了对全局状态的直接耦合. 这很不好.
  • 这种耦合就导致很难对测试进行设置. 针对每个测试, 我们必须创建和设置好存储全局状态的对象. 或者把全局变量设定为所需的值.
  • 因为每个全局状态变量在内存里只有一个实例, 那么我们就无法进行并行单元测试了. 如果我们为A测试设定了全局变量的值, 然后在测试A结束前开始测试B, 这时测试B修改了全局变量的值, 这时测试A就可能会失败, 因为它所期待的全局变量不是这个值.
  • 上面的这种现象就叫做鬼魅般的超距作用(Spooky Action at a Distance). 而实际项目中确实经常发生这样的情况, 并行跑单元测试的时候偶尔会失败, 而单独去跑失败的测试时却一直成功. 这种耦合到全局状态的测试就不能再称为隔离测试了.

危险信号

  • 全局变量
  • 调用静态字段或调用拥有静态字段的类的静态方法. 但也仅限于该类的静态方法使用了该类的静态字段.
  • 单例模式 (Singleton Pattern)
  • 单元测试会随机的失败, 但是又没发现明确的原因.

解决办法

  • 尽量使用本地(局部, 越窄越好)状态变量
  • 如果第三方库使用了静态方法, 那么应该使用一个包装类来对该方法进行包装. 这个包装类还是要实现一个接口. 用它的时候注入该接口即可. 这样测试的时候就可以为包装类创建测试替身了, 并把全局状态解耦.
  • 使用可依赖注入(IoC/DI)的单例体, 这种单例体是由IoC容器创建的.

例子

就举一个例子吧.

有这样一个获取当前登录用户权限的类, 它使用的是单例模式:

这个是典型的单例模式, 它会保证在程序中只返回一个实例, 这里就不多介绍了.

下面这个Service会调用上面这个Auth类:

Auth是单例模式的, 而且还调用了静态方法.

现在的状态是, OfficeService和Auth所包含的全局状态紧密的耦合到了一起.

如何解决问题

首先应该把单例模式去掉, Auth类只保留两个属性和一个方法:

然后在service里面应该注入IAuth接口并使用:

那么接下来就需要保证这个IAuth无论在程序中注入了多少次, 都是同一个实例.

这时就需要使用依赖注入(DI) 库了. 现在的DI库通常允许指定IoC容器中每对绑定服务的作用范围(Scope), 或叫做生命周期管理.

例如ASP.NET Core内置的IoC容器就内置了这种功能. 在ASP.NET Core 项目的Startup类里, 这样写就可以保证每次请求IAuth的时候只会得到同一个对象实例:

现在这个"单例"的工作是由IoC容器来负责了. 在其它地方正常的注入IAuth使用即可.

先写到这, 本文的概念性内容和更多的例子请参考Angular创始的人这篇文章: http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/

.NET Core TDD 前传: 编写易于测试的代码 -- 全局状态的更多相关文章

  1. .NET Core TDD 前传: 编写易于测试的代码 -- 缝

    有时候不是我们不想做单元测试, 而是这代码写的实在是没法测试.... 举个例子, 如果一辆汽车在产出后没完成测试, 那么没人敢去驾驶它. 代码也是一样的, 如果项目未能进行该做的测试, 那么客户就不敢 ...

  2. .NET Core TDD 前传: 编写易于测试的代码 一 -- 缝

    转载于: https://www.cnblogs.com/cgzl/p/9365955.html 有时候不是我们不想做单元测试, 而是这代码写的实在是没法测试.... 举个例子, 如果一辆汽车在产出后 ...

  3. .NET Core TDD 前传: 编写易于测试的代码 -- 单一职责

    第1篇: 讲述了如何创造"缝".  "缝"(seam)是需要知道的概念. 第2篇, 避免在构建对象时写出不易测试的代码. 第3篇, 依赖项和迪米特法则. 第4篇 ...

  4. .NET Core TDD 前传: 编写易于测试的代码 -- 构建对象

    该系列第1篇: 讲述了如何创造"缝".  "缝"(seam)是需要知道的概念. 本文是第2篇, 介绍的是如何避免在构建对象时写出不易测试的代码. 本文的概念性内 ...

  5. .NET Core TDD 前传: 编写易于测试的代码 -- 依赖项

    第1篇: 讲述了如何创造"缝".  "缝"(seam)是需要知道的概念. 第2篇, 避免在构建对象时写出不易测试的代码. 本文是第3篇, 讲述依赖项和迪米特法则 ...

  6. 新书《编写可测试的JavaScript代码 》出版,感谢支持

    本书介绍 JavaScript专业开发人员必须具备的一个技能是能够编写可测试的代码.不管是创建新应用程序,还是重写遗留代码,本书都将向你展示如何为客户端和服务器编写和维护可测试的JavaScript代 ...

  7. 编写可测试的JavaScript代码

    <编写可测试的JavaScript代码>基本信息作者: [美] Mark Ethan Trostler 托斯勒 著 译者: 徐涛出版社:人民邮电出版社ISBN:9787115373373上 ...

  8. 从一张图开始,谈一谈.NET Core和前后端技术的演进之路

    从一张图开始,谈一谈.NET Core和前后端技术的演进之路 邹溪源,李文强,来自长沙.NET技术社区 一张图 2019年3月10日,在长沙.NET 技术社区组织的技术沙龙<.NET Core和 ...

  9. 编写Avocado测试

    编写Avocado测试 现在我们开始使用python编写Avocado测试,测试继承于avocado.Test. 基本例子 创建一个时间测试,sleeptest,测试非常简单,只是sleep一会: i ...

随机推荐

  1. jQuery学习之旅 Item5 $与jQuery对象

    1.$符号的由来 $符号本质就是函数的名字. jquery源码分析 通过分析我们知道,在jquery里边不只可以使用$符号,还可以使用jQuery标志 解决冲突问题 有的项目是中间过渡项目(proto ...

  2. 『网络の转载』关于初学者上传文件到github的方法

    说来也惭愧,我是最近开始用github,小白一个,昨天研究了一个下午.终于可以上传了,所以今天写点,一来分享是自己的一些经验,二来也是做个记录,万一哪天又不记得了:) 废话不多说,直接来,这次主要介绍 ...

  3. javascript 正则表达式补充

    定义 JavaScript种正则表达式有两种定义方式,定义一个匹配类似 <%XXX%> 的字符串 1. 构造函数 var reg=new RegExp('<%[^%>]+%&g ...

  4. 巩固java(五)----通过实例理解java多态

    package duotai; class A{ public String show(){ return "A"; } } class B extends A{ public S ...

  5. left join,right join,inner join,full join之间的区别

    参考 https://www.cnblogs.com/assasion/p/7768931.html https://blog.csdn.net/rongbo_j/article/details/46 ...

  6. C++中常用到的容器

    这里主要讲C++中经常用到的一些保存数据的容器,其中也会介绍string. 在C++11中提到了很多容器,这里主要介绍:vector.list.map.还有一些其他的容器就不做介绍了. 1.Strin ...

  7. 【爆料】-《卧龙岗大学毕业证书》UOW一模一样原件

    ☞西悉尼大学毕业证书[微/Q:865121257◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&a ...

  8. python3——模块

    今天去听一个关于创业的讲座,心疼自己在那个地方站了 一个多小时(QAQ)我是心疼自己傻呀! 手机打王者之后就没有电了,一直站在那儿! 不过最后还是结束了,以后你们也会有很多讲座的,希望学弟学妹好好听讲 ...

  9. Postman-----设置环境变量

    1.环境变量的作用域: 使用环境变量(可用于切换开发环境/生产环境.设置动态参数),有4个作用域(优先级由高到低):Global, Environment, Local, Data.同名变量会使用优先 ...

  10. Hadoop3.0 WordCount测试一直Accept 状态,Nodes of the cluster 页面node列表个数为0

    起因是我运行wordcount测试一直卡主,不能执行,一直处于 Accept 状态,等待被执行,刚开始是各种配置yarn参数,以及host配置,后来发现还是不行 hadoop 集群安装完成后,在500 ...