.NET Core TDD 前传OA 信用盘平台搭建: 编写易于测试的代码构建对象

联系方式:QQ:2747044651 网址http://zhengtuwl.com
常情况下, 我们是先去建造汽车, 组装好汽车后, 我们再去驾驶它.
软件开发也类似, 我们应该把对象构造完毕之后, 再去用它. 但是有时候, 开发者会在构造过程中添加一些程序逻辑. 这就相当于车还没造完, 我们就驾驶它去兜风了. 这样做是不太好的.
构造函数是类用来创建其实例对象的方法, 这里的代码是用来准备该对象的. 但有时开发者会在构造函数里做一些其它的工作, 例如构建依赖项, 执行初始化逻辑等等.
在构造函数(或者更大一点, 指构建的过程)里, 做这些额外的工作会让测试变得异常困难. 这是因为像初始化依赖项, 调用服务, 设置状态的逻辑等这些工作会把用于测试的”缝”弄丢. 导致无法进行mock.
总之在构造的过程中做太多的工作会妨碍测试.
危险信号
在构造函数/字段声明里出现new关键字
如果构造函数里需要创建依赖, 那么这就会为该类与依赖项之间创造了紧耦合. 这个之前提过, 所以需要注入依赖. 但是简单的值类型, 例如字符串, List, Dictionary等还是可以的.
在构造函数/字段声明里调用静态方法
静态方法不可以被mock, 也不能被注入.
构造函数出现流程控制逻辑代码
这样就很难对逻辑直接进行测试了. 我们只能分别使用不同的方式构造该对象, 测试并确认对象的状态. 而这个状态通常对直接测试是隐藏的. 实际上只要不是赋值代码, 就有可能是问题代码.
构造函数里出现非赋值代码
存在另外一个初始化函数 (也就是说构造函数走了完, 但是对象并没有被完全初始化)
如何解决问题?
不要在构造函数里创建依赖项, 应该注入它们. 然后在构造函数里把它们赋值给类的私有变量.
当需要构建对象图(一组有引用关系的对象), 也包括对象需要一些构建的参数等情况, 应该使用工厂, 建造者模式, 或者IoC容器的依赖注入等, 目的是把这些对象的构建工作分离出去.
避免在构造函数里写逻辑代码, 例如条件, 循环, 计算等等. 也不能把逻辑代码放在别的方法, 然后调用该方法…
总之就是要避免对象的构建和对象的行为混合到一起, 因为它们在一起就会很难进行测试.
最后还有一点, 首先你需要知道, 根据angular的创始人Misko Hevery所说:
对象的构造分两类, 一种是可注入的, 一种是可new的.
可注入的对象可以由其它的一堆可注入对象组成. 它们可以为 可new的 对象工作. 可注入的对象通常是实现了接口的service, 像什么IUnitOfWork, IRepository, IxxxService等等.
可new的对象就是对象图里的终点, 例如实体或者值对象(Value Object)等.
为了易于测试, 针对这两类构造, 有下列规则:
可注入的对象可以在构造函数请求(注入)其它的可以注入对象, 但是不能在构造函数请求可new的对象.
反过来, 可new的对象可以在构造函数请求其它的可new对象, 但是不能在构造函数请求可注入的对象.
例子
第一个例子
这是不对的, 构建的过程中直接new的话, 就会造成紧耦合, 也无法在测试中使用Test Double来代替它们了. 如果测试中不代替它们的话, 有些服务的开销可能会很大.
正确的写法是使用依赖注入:
第二个例子
该例中, UserController只需要UserService和LoggingService两个依赖项. 但是UserService又依赖于UserRepository.
但是这样写就不对了, 这会造成UserController和UserRepository间的紧耦合, 而且配置UserService也并不是UserController的责任.

正确的写法是:

而UserService也最好是注入依赖.

而如果UserService并不是在构造函数注入UserRepository的话:

那么Controller里就应该这样写:

不过最好还是使用构造函数注入的写法.

第三个例子

仔细的说, 该例有不止一处错误.

首先它有条件判断逻辑代码; 此外它还使用了ApplicationState.IsRunning这个静态变量(就是全局状态); 而且在构造函数里还做了UserService的配置工作, 这不是UserController的责任.

尽量要避免全局变量, 它无法进行隔离, 测试会遇到麻烦, 例如并行测试时其中一个测试改变了静态变量的值就可能导致另一个测试失败.

但是粗略的说, 该例可以说就是一个错误, 如何配置UserService并不是UserController的责任, 所以, 正确的做法是把UserService配置相关的代码移出去, 让它自己去管理吧:

第四个例子

该例子中, LoggingService的Log方法需要一个Area类型的对象, 它是一个值对象.

所以它的错误就是, 不应该把可new的对象注入到可注入的对象里. 这么做的话, 测试就不好做隔离了.

正确的做法应该是, 作为方法的参数传递进来:

第五个例子
如果出现类类似initalize()或类似意思的方法, 很有可能说明该对象的责任太多了.

修改它很简单, 让各自的类负责自己的内容即可. 去掉initialize()方法即可.

例子就举这些, 并不全, 详细请看Angular作者的博文.

测试/运行时如何建立对象
上面例子里的UserController就是我们需要使用的对象, 在运行时, 代码可能是这样的:

构建这个对象还是有点麻烦的, 它的类关系图如下:

所以测试的设置过程也会比较麻烦:

当然也可以不直接new, 而是使用mock. 总之都很麻烦.

使用工厂
所以我们可以使用Factory等模式, 把构建UserController的工作放到工厂里:

可以这样调用:erRepository.
但是这样写就不对了, 这会造成UserController和UserRepository间的紧耦合, 而且配置UserService也并不是UserController的责任.

正确的写法是:

而UserService也最好是注入依赖.

而如果UserService并不是在构造函数注入UserRepository的话:

那么Controller里就应该这样写:

不过最好还是使用构造函数注入的写法.

第三个例子

仔细的说, 该例有不止一处错误.

首先它有条件判断逻辑代码; 此外它还使用了ApplicationState.IsRunning这个静态变量(就是全局状态); 而且在构造函数里还做了UserService的配置工作, 这不是UserController的责任.

尽量要避免全局变量, 它无法进行隔离, 测试会遇到麻烦, 例如并行测试时其中一个测试改变了静态变量的值就可能导致另一个测试失败.

但是粗略的说, 该例可以说就是一个错误, 如何配置UserService并不是UserController的责任, 所以, 正确的做法是把UserService配置相关的代码移出去, 让它自己去管理吧:

第四个例子

该例子中, LoggingService的Log方法需要一个Area类型的对象, 它是一个值对象.

所以它的错误就是, 不应该把可new的对象注入到可注入的对象里. 这么做的话, 测试就不好做隔离了.

正确的做法应该是, 作为方法的参数传递进来:

第五个例子
如果出现类类似initalize()或类似意思的方法, 很有可能说明该对象的责任太多了.

修改它很简单, 让各自的类负责自己的内容即可. 去掉initialize()方法即可.

例子就举这些, 并不全, 详细请看Angular作者的博文.

测试/运行时如何建立对象
上面例子里的UserController就是我们需要使用的对象, 在运行时, 代码可能是这样的:

构建这个对象还是有点麻烦的, 它的类关系图如下:

所以测试的设置过程也会比较麻烦:

当然也可以不直接new, 而是使用mock. 总之都很麻烦.

使用工厂
所以我们可以使用Factory等模式, 把构建UserController的工作放到工厂里:

可以这样调用:

阅读更多

原文地址:http://blog.51cto.com/13914483/2156340

时间: 2024-11-09 03:05:37

.NET Core TDD 前传OA 信用盘平台搭建: 编写易于测试的代码构建对象的相关文章

JDK动态OA信用盘平台出租代理和cglib动态代理

一.代理设计模式OA信用盘平台出租haozbbs.com Q1446595067 代理类和委托类具有相同的接口.代理类的对象本身并不真正实现服务,而是通过委托类的对象的相关方法来提供特定的服务. 二.静态代理 见<大话设计模式>第7章 缺点: 一个代理类只能应用于一个接口的实现类,如果有多个接口的话就要定义很多实现类和代理类才行.而且,如果代理类对业务方法的预处理.调用后操作都是一样的(比如:调用前输出提示.调用后自动关闭连接),则多个代理类就会有很多的重复代码.这时我们可以定义这样一个代理类

由Trust Wallet理解以OA信用盘平台出售带杀大赔小功能太坊钱包管理和智能合约

钱包管理钱包管理就要提到OA信用盘平台出售带杀大赔小功能QQ2952777280 话仙论坛http://hxforum.com 一个类EtherKeystore, 应用的核心业务的处理类, 有钱包管理(创建.删除.导入.导出).助记词转化.签名工作.私钥管理等功能.EtherKeystore中使用了由Trust开源的了两个库: TrustKeystore: 用于管理钱包的通用以太坊密钥库.TrustCore: 区块链核心的数据结构和算法.还有CryptoSwift, 一个标准的安全加密算法集合的

Oracle数据OA信用盘平台出租远程连接的四种设置方法和注意事项

OA信用盘平台出租论坛:haozbbs.com Q1446595067 第一种情况: 若oracle服务器装在本机上,那就不多说了,连接只是用户名和密码的问题了.不过要注意环境变量%ORACLE_HOME%/network/admin/是否设置. 第二种情况: 本机未安装oracle服务器,也未安装oracle客户端.但是安装了pl sql development.toad sql development.sql navigator等管理数据库的工具.在虚拟机或者另一台电脑上安装了oracle服

使用Cmake生成OA信用盘平台出租跨平台项目编译解决方案

项目最近OA信用盘平台出租haozbbs.comQ1446595067 有需求在windows下面运行,我花了几周时间将linux的服务器移植到windows下面,目前已经能够正常运行服务器,目前又有了新需求,两边的代码结构和组织是分开的,因此为了两边能够同步维护,需要一个能够跨平台的项目编译解决方案,经过调研之后,选择了使用cmake这个工具,本文主要讲述,使用cmake的生产项目的一些基础知识.一.cmake简介 你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmak

Python属性和OA信用盘平台租用

属性有两种,类属性,实例属性.OA信用盘平台租用(企 娥:217 1793 408) 给类下所有的对象添加属性,可以添加类属性,给对象添加的实例属性,类下的其他对象,并不会获得这个属性. class Person(object):pass Person.sex = "male"#所有对象都能获得p1 = Person()p1.age = 12p2 = Person()print(p2.sex)#maleprint(p2.age)#报错AttributeError: 'Person' o

Eureka 2.X 停止开发,但注册中心OA信用盘平台制作还有更多

在上个月我们知道 Eureka 2.X 遇到困难停止OA信用盘平台制作QQ2952777280[话仙源码论坛]hxforum.com[木瓜源码论坛]papayabbs.com开发了,但其实对国内的用户影响甚小,一方面国内大都使用的是 Eureka 1.X 系列,另一方面 Spring Cloud 支持很多服务发现的软件,Eureka 只是其中之一,下面是 Spring Cloud 支持的服务发现软件以及特性对比: Feature euerka Consul zookeeper etcd服务健康检

ArrayList原理、OA信用盘平台出租LinkedList原理和方法和迭代器注意事项

迭代器在变量元素OA信用盘平台出租QQ2952777280[话仙源码论坛]hxforum.com[木瓜源码论坛]papayabbs.com的时候要注意事项: 在迭代器迭代元素 的过程中,不允许使用集合对象改变集合中的元素个数,如果需要添加或者删除只能使用迭代器的方法进行操作. 如果使用过了集合对象改变集合中元素个数那么就会出现ConcurrentModificationException异常. 迭代元素的过程中: 迭代器创建到使用结束的时间. ------意识是迭代器一旦创建,在之后是不可以用集

C#扩展OA信用盘平台维护的理解

"扩展方法使您能够向现有类型"添加"方法,OA信用盘平台维护(企 娥:217 1793 408)而无需创建新的派生类型.重新编译或以其他方式修改原始类型." 这是msdn上说的,也就是你可以对String,Int,DataRow,DataTable等这些类型的基础上增加一个或多个方法,使用时不需要去修改或编译类型本身的代码. 先做个例子吧,以String为例,需要在字符串类型中加一个从字符串转为数值的功能. 以往我们可能是这样做的,会专门写一个方法做过转换 publ

Typescript 和 Javascript之间OA信用盘平台出租的区别

JavaScript 和 TypeScript 的概要介绍JavaScript 是一种轻量级的解释性脚本语言OA信用盘平台出租QQ2952777280[话仙源码论坛]hxforum.com[木瓜源码论坛]papayabbs.com,可嵌入到 HTML 页面中,在浏览器端执行,能够实现浏览器端丰富的交互功能,为用户带来流畅多样的用户体验. JavaScript 是基于对象和事件驱动的,无需特定的语言环境,只需在支持的浏览器上就能运行. JavaScript 语言具有以下特点: JavaScript