.NET Core TDD 前传: 编写易于测试的代码 -- 全局状态

.NET Core TDD 前传: 编写易于测试的代码 -- 全局状态,第1张

概述第1篇: 讲述了如何创造"缝".  "缝"(seam)是需要知道的概念. 第2篇, 避免在构建对象时写出不易测试的代码. 第3篇, 依赖项和迪米特法则. 本文是第4篇, 将介绍全局状态引起的问题.   全局状态 全局状态, 也可以叫做应用程序状态, 它是一组变量, 这些变量维护着应用程序的高级状态. 在程序里, 全局状态可能都存放在一个全局状态对象里, 例如ASP.NET里面的HttpContex

第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 前传: 编写易于测试的代码 -- 全局状态全部内容,希望文章能够帮你解决.NET Core TDD 前传: 编写易于测试的代码 -- 全局状态所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1230195.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存