Go项目的测试代码3(测试替身Test Double)
上一篇文章介绍了项目中测试代码的写法。
这里简单的共享一下测试替身。
当我们写测试代码的时候,经常遇到一个问题。
跟别的模块或服务有依赖性,可是功能还没开发完。
或是因为各种原因(安全,白名单,内网等等…)开发环境里无法调用相应的模块。
这时候我们应该如何去写测试代码?
因此测试替身Test Double出现了。 其功能和名字一样,做替身。
测试替身Test Double的分以下类型。 Dummy, Stub, Fake, Spy, Mock
Dummy
最简单、最原始的测试替身型别。Dummy 没有实作,最常用于需要参数值但不使用它的情况。
Null 可视为是 Dummy,但真的 Dummy 是接口或基类的衍生,且完全不包含实作。
Stub
Dummy 的上一级,Stub 是接口或基类的最低限度实作。
会传回 Void 的方法通常完全不包含实作,而会传回值的方法通常会传回硬式编码的值。
Spy
测试 Spy 类似 Stub,但除了提供客户端可叫用成员的实例,
Spy 还会记录叫用了哪些成员,好让单元测试验证所叫用的成员是否符合预期。
Fake
Fake 包含更复杂的实作,通常涉及所继承型别之不同成员之间的互动。
虽然不是完整的生产实作,但 Fake 与生产实作很相似,尽管它会采取一些快捷方式。
Mock
Mock 是由 Mock 链接库动态建立 (其他通常是由测试开发人员使用程序代码来产生)。
测试开发人员永远看不到实作接口或基类的实际程序代码,但是可以设定 Mock 以提供传回值、预期要叫用的特定成员…等等。
视其中的设定而定,Mock 的行为可能会像 Dummy、Stub 或 Spy。
以上是网上流行的概念和理论。
我在项目中的运用
我开发的是在企业微信中的应用,所以需要调用企业微信的API,
用过的人应该知道,这需要登记可信域名(简单的说不能本地调用,只能发布到服务器才能调用)。
也可以用第三方工具去模拟调试,但是这么做感觉很不方便。而且Debug也相当麻烦。
我是这么处理的。
项目初期(一阶段)
直接写硬代码hardcode。
一般我们都会说尽量避免写硬代码hardcode,
但是项目初期我建议大家这么写。
为什么? 其实很简单。
初期我们应该把所有的精力放在自己模块的功能和稳定性。
没必要在别人的接口里耗精力, 甚至去考虑怎么和其他模块做联动。
例子:
var key string
if config.AppEnv == "test" || config.AppEnv == "dev" { //测试或是开发环境的时候直接赋值
key = ""
} else {
key, err = (Login{}).GetKeyForLogin(ctx, code) //实际调用的接口写在这里
if err != nil {
return nil, err
}
}
项目中期(二阶段)
硬代码hardcode改写函数function。
可以写根据参数或设定返回不同值的方法。满足不同场景下有不同返回值的需求。
例子:
var key string
if config.AppEnv == "test" || config.AppEnv == "dev" {
key = GetKeyForLoginForTest(ctx, code) //测试或是开发环境的时候直接赋值
} else {
key, err = (Login{}).GetKeyForLogin(ctx, code) //实际调用的接口写在这里
if err != nil {
return nil, err
}
}
func GetKeyForLoginForTest(ctx context.Context, code string) string {
var key string
switch code {
case "code1":
key = "key1"
case "code2":
key = "key2"
case :
......
default:
key = "key3"
}
return key
}
项目后期(三阶段)
函数function升类或package。
这个阶段是可选项,因为这里开始需要耗精力去管理自己的“替身”了。
根据自己的实际情况来决定要不要这么做。
例子:
type LoginTest interface {
GetKeyForLoginForTest(ctx context.Context, code string) string
}
type SellerTest struct {
}
func (SellerTest) GetKeyForLoginForTest(ctx context.Context, code string) string {
//销售员业务处理...
return "SellerKey"
}
type ManagerTest struct {
}
func (ManagerTest) GetKeyForLoginForTest(ctx context.Context, code string) string {
//业务处理...
return "ManagerKey"
}
func BuildManagedObject(ctx context.Context, condition string) string {
switch condition {
case "condition1":
return SellerTest{}
case "condition2":
return ManagerTest{}
case :
......
default:
return SellerTest{}
}
}
var key string
if config.AppEnv == "test" || config.AppEnv == "dev" {
var loginTest LoginTest
loginTest = BuildManagedObject(condition)
key = loginTest.GetKeyForLoginForTest(ctx, code) //测试或是开发环境的时候直接赋值
} else {
key, err = (Login{}).GetKeyForLogin(ctx, code) //实际调用的接口写在这里
if err != nil {
return nil, err
}
}
这么做其实能解决大部分的问题。
不知道我做的项目小或范围小,总之还没有碰到过用以上方式不能解决的问题。有什么不太恰当的部分随时欢迎指正。
组员不愿意写测试代码而找借口的时候你可以这么说了…^^
组员:这次项目依赖性很高, 其他模块的功能还没完善, 开发都很麻烦别说写测试代码了。
我:你用过测试替身吗?可以很简单的解决这些问题。
Go项目的测试代码3(测试替身Test Double)的更多相关文章
- 使用强大的 Mockito 测试框架来测试你的代码
原文链接 : Unit tests with Mockito - Tutorial 译文出自 : 掘金翻译计划 译者 : edvardhua 校对者: hackerkevin, futureshine ...
- iOS开发:XCTest单元测试(附上一个单例的测试代码)
测试驱动开发并不是一个很新鲜的概念了.在我最开始学习程序编写时,最喜欢干的事情就是编写一段代码,然后运行观察结果是否正确.我所学习第一门语言是c语言,用的最多的是在算法设计上,那时候最常做的事情就是编 ...
- Dlib库中实现正脸人脸关键点(landmark)检测的测试代码
Dlib库中提供了正脸人脸关键点检测的接口,这里参考dlib/examples/face_landmark_detection_ex.cpp中的代码,通过调用Dlib中的接口,实现正脸人脸关键点检测的 ...
- Dlib库中实现正脸人脸检测的测试代码
Dlib库中提供了正脸人脸检测的接口,这里参考dlib/examples/face_detection_ex.cpp中的代码,通过调用Dlib中的接口,实现正脸人脸检测的测试代码,测试代码如下: #i ...
- Tars | 第7篇 TarsJava Subset最终代码的测试方案设计
目录 前言 1. SubsetConf配置项的结构 1.1 SubsetConf 1.2 RatioConfig 1.3 KeyConfig 1.4 KeyRoute 1.5 SubsetConf的结 ...
- 入门级----黑盒测试、白盒测试、手工测试、自动化测试、探索性测试、单元测试、性能测试、数据库性能、压力测试、安全性测试、SQL注入、缓冲区溢出、环境测试
黑盒测试 黑盒测试把产品软件当成是一个黑箱子,只有出口和入口,测试过程中只要知道往黑盒中输入什么东西,知道黑盒会出来什么结果就可以了,不需要了解黑箱子里面是如果做的. 即测试人员不用费神去理解软件里面 ...
- Go项目的测试代码2(项目运用)
上一篇文章介绍了最基本的测试代码的写法.Go项目的测试代码(基础) 这里简单的共享一下我在项目中使用的方式. 项目结构 我们实际项目中, 结构简单地分了控制层controllers和模块层models ...
- Go项目的测试代码1(基础)
最近写了测试代码,整理了一下. 先看看简单的测试代码. // add_test.go ==> 文件名 _test.go 结尾的默认为测试代码文件 package models import ( ...
- 使用Jenkins结合Gogs和SonarQube对项目代码进行测试、部署、回滚,以及使用keepalived+haproxy调度至后端tomcat
0 环境说明 主tomcat:192.168.0.112 备tomcat:192.168.0.183 haproxy+keepalived-1:192.168.0.156 haproxy+keepal ...
随机推荐
- mORMot学习笔记3 数据集转Json
usesSynCommons, SynDB, SynOleDB; procedure TForm1.DataToJsonClick(Sender: TObject); var Conn: TOleDB ...
- 微信公众号开发(三)—— access_token的管理
上一篇 微信公众号开发(二)—— 微信公众平台接入 让我们的本地工程顺利的接入到微信公众号系统, 那么接下啦我们介绍一个很重要的感念——acess_token (access_token是公众号的全局 ...
- Hadoop_19_MapReduce&&Yarn运行机制
1.YARN的运行机制 1.1.概述: Yarn集群:负责海量数据运算时的资源调度,集群中的角色主要有:ResourceManager.NodeManager Yarn是一个资源调度(作业调度和集群资 ...
- 03_Redis_String命令
一:Redis命令---String命令:Redis 字符串数据类型的相关命令用于管理 redis 字符串值 字符串类型是Redis中最为基础.常用的数据存储类型,字符串在Redis中是二进制安全的, ...
- java_变量和常量
一.变量(可以改变的量) 1.命名规则: a.遵循标识符命名规则: 1.关键字是不能用作标识符的 2.区分大小写 3.可以包含数字.字母.下划线.美元符号$,但是不能以数字作为开头 b.尽量使用有意义 ...
- linux—-远程连接——管理工具
1,linux服务器需要安装ssh服务端,端口一般22 2,使用ssh客户端连接linux服务器: 常用工具: putty xshell 3,上传文件工具: 使用sftp,linux需要安装sftp服 ...
- Python数据库连接池 -组件 DBUtils
DBUtils是Python的一个用于实现数据库连接池的模块 此连接池有两种连接模式: DBUtils提供两种外部接口: PersistentDB :提供线程专用的数据库连接,并自动管理连接. Poo ...
- keeping
很多时候我们总是低估了自己,对自己不够狠,从而错过了遇到一个更加优秀的自己.逼自己一把,很多事并不需要多高的智商,仅仅需要你的一份坚持,一个认真的态度,一颗迎难而上的决心
- SpringDatajpa 使用原生的SQL进行分组查询
话不多说,直接上代码 dao nativeQuery = true ---> 执行原生的SQL语法,也就是说这段sql拷贝到数据库中,然后就运行. 我们期望的结果: 取值: 取值结果: 结合实际 ...
- Beep调用系统声音
using System.Runtime.InteropServices; 引用命名空间 [DllImport("kernel32.dll")]public static ...