Go项目的测试代码2(项目运用)
上一篇文章介绍了最基本的测试代码的写法。
Go项目的测试代码(基础)
这里简单的共享一下我在项目中使用的方式。
项目结构
我们实际项目中, 结构简单地分了控制层controllers
和模块层models
因为现在都已微服务的形式开发,没必要太复杂的结构。
分控制层和模块层已经能满足我们的需求
,不需要再细分了
。
|___config ||==> 配置文件
| |___config.qa.go
| |___config.production.go
... ...
|___controllers ||==> 控制层,只做参数的有效性和简单的逻辑处理
| |___app_api_test.go
| |___app_api.go
| |___init_test.go
| |___tenant_api.go
... ...
|___models ||==> 模块层,所有的业务都是在模块层里实现的
| |___app.go
| |___app_test.go
| |___init_test.go
| |___tenant.go
... ...
|___main.go ||==> 系统的入口
|___factory |||
|___kit ||| ==> 按需求自定义建立一个package方便使用
|___filters |||
... ...
看项目结构的话,可以发现测试方法直接写在对应的package里面
有些人喜欢把测试方法分另一个package里写,也可以~不同人有不同的喜好。
我也试过这种方式,但是还是喜欢写在同一个package里面
,这么写简单方便
控制层controllers和模块层models的不同方式
控制层controllers
控制层是提供接口api的直接入口,所以那些重要的api都需要写测试方法。
我喜欢在这里写参数验证
,业务验证
等等主要功能的测试
。
模块层models
因为控制层controllers里已经做了业务验证了,所以再次做业务验证感觉是多余的。
我喜欢在这里写方法的逻辑测试
,一般和数据没太多关系。
或很复杂的业务处理
的话也会在这里写测试,和控制层做双重确认
。
控制层controllers的测试代码
init() 函数
每个package里有 init_test.go文件
这里写 init()方法 执行 go test的时候的初始化方法
。
package controllers var (
appEnv = flag.String("app-env", os.Getenv("APP_ENV"), "app env")
ctx context.Context
echoApp *echo.Echo
xormEngine *xorm.Engine
) func init() {
//执行1个线程
runtime.GOMAXPROCS()
var err error
//测试数据的链接是内存里的sqlite ==> 每次执行测试代码的时候用到的是内存的sqlite 所以不用管理数据库
xormEngine, err = xorm.NewEngine("sqlite3", ":memory:")
if err != nil {
panic(err)
}
//读取配置
var c configutil.Config
if err := configutil.Read("", &c, configutil.TestMode); err != nil {
panic(err)
} //建立Redis链接 没有用到redis可以跳过
models.SetRedisConn(os.Getenv("TEST_SERVICE_REDIS_CONNECTION"))
//登记struct到xormEngine
models.SetXormEngineSync(xormEngine)
//初始化数据
models.Seed(xormEngine, configutil.TestMode) //echo架构
echoApp = echo.New()
echoApp.Validator = &filters.Validator{} //为了测试先模拟一个context
ctx = context.WithValue(context.Background(), echomiddleware.ContextDBName, xormEngine.NewSession())
}
Seed() 函数
我觉得写测试代码的时候,制作测试数据
的时间比会占50%以上
。
让人最头疼的事情。
刚开始的时候我觉得可以直接写。
如下:
var (
tenants = []Tenant{
{Id: , Code: "eland", Name: "上海衣恋", Enable: true},
{Id: , Code: "T02", Name: "Ice cream", Enable: true},
} brands = []Brand{
{Id: , Code: "EE", Name: "Eland", Enable: true},
{Id: , Code: "EK", Name: "Eland Accessory", Enable: true},
{Id: , Code: "IC", Name: "Haagen-Dazs", Enable: true},
}
... ...
)
func Seed(xormEngine *xorm.Engine) {
for _, u := range tenants {
xormEngine.Insert(&u)
}
for _, u := range brands {
xormEngine.Insert(&u)
}
... ...
}
但是当有一些mapping表
的话这么加数据会是个噩梦…ㅠㅠ
这时候可以先把数据添加到mysql数据库
(使用流行的工具应该还不错吧?)。sqlite
的可视化工具太难用果断放弃。
func Seed(xormEngine *xorm.Engine, executeMode configutil.ExecuteMode) {
//建立原始数据的连接
driverName := os.Getenv("TEST_SERVICE_DRIVER")
dataSourceName := os.Getenv("TEST_SERVICE_CONNECTION")
testXormEngine, err := xorm.NewEngine(driverName, dataSourceName)
if err != nil {
panic(err)
} //登记struct
SetXormEngineSync(testXormEngine) //数据保存到内存的sqlite
Tenant_SetTestData(testXormEngine, xormEngine)
Brand_SetTestData(testXormEngine, xormEngine)
... ...
} func SetXormEngineSync(xormEngine *xorm.Engine) {
xormEngine.Sync(new(Tenant))
xormEngine.Sync(new(Brand))
... ...
} func Tenant_SetTestData(testXormEngine *xorm.Engine, xormEngine *xorm.Engine) {
var tenants []Tenant
if err := testXormEngine.Find(&tenants); err != nil {
fmt.Println(err)
}
for _, u := range tenants {
xormEngine.Insert(&u)
}
}
... ...
多人做的项目如何管理原始数据?
我是这么做的
- 准备一个
公用的mysql数据库
(没有的话可以让下班最晚的员工开数据库共享…^^) - 在公共的数据库里
添加原始数据。
- 每个组员都可以
灵活利用Seed方法
,把数据导入到自己本地数据库。
不嫌慢的话直接调用公用数据库。(也可以使用docker镜像)
(Seed方法的原始数据库地址和目标数据库地址可以灵活地去修改,导数据非常easy) - 如果原始数据有变化,组员之间
相互共享
,从新导入。
接口api测试代码
繁琐的准备工作已经做完,终于可以写测试代码了…^^
func Test_ColleagueApiController_GetColleagues(t *testing.T) {
//需要测试的api
req := httptest.NewRequest(echo.GET, "/api/v1/colleagues/:id", nil) //添加token和context
c, rec := SetContextWithToken(req) //执行完测试代码后需要回滚
defer factory.DB(c.Request().Context()).Close()
defer factory.DB(c.Request().Context()).Rollback()
factory.DB(c.Request().Context()).Begin() //参数设定
c.SetParamNames("id")
c.SetParamValues("") //调用api方法
test.Ok(t, ColleagueApiController{}.GetBrandShops(c))
test.Equals(t, http.StatusOK, rec.Code) //api返回结果的结构
var v struct {
Result struct {
ColleagueId int64 `json:"colleagueId"`
ColleagueNo string `json:"colleagueNo"`
Name string `json:"name"`
} `json:"result"`
Success bool `json:"success"`
} //验证结果
test.Ok(t, json.Unmarshal(rec.Body.Bytes(), &v))
test.Equals(t, v.Result.ColleagueId, int64())
test.Equals(t, v.Result.ColleagueNo, "C000001")
test.Equals(t, v.Result.Name, "测试人员")
}
模块层models的测试代码
init() 函数
和controllers 的init()方法没什么区别
var (
appEnv = flag.String("app-env", os.Getenv("APP_ENV"), "app env")
xormEngine *xorm.Engine
) func init() {
runtime.GOMAXPROCS()
var err error
xormEngine, err = xorm.NewEngine("sqlite3", ":memory:")
if err != nil {
panic(err)
}
var c configutil.Config
if err := configutil.Read("", &c, configutil.TestMode); err != nil {
panic(err)
}
SetModelConfig(&ModelConfig{ValidTimeout: c.ValidTimeout, AppEnv: *appEnv})
SetRedisConn(os.Getenv("TEST_SERVICE_REDIS_CONNECTION"))
SetXormEngineSync(xormEngine) Seed(xormEngine, configutil.TestMode)
}
多写了一个GetContext()方法
如果需要数据连接先调用。
func GetContextForTest() context.Context {
return context.WithValue(context.Background(), echomiddleware.ContextDBName, xormEngine.NewSession())
}
测试代码
模块层models的测试代码如下:
func Test_GetTenantAppInfosFromAppContainers(t *testing.T) {
// 因为这个方法不需要连数据库所以也不需要这些操作
// ctx := GetContextForTest()
// factory.DB(ctx).Begin()
// defer factory.DB(ctx).Close()
// defer factory.DB(ctx).Rollback() appContainers := []TenantAppContainer{
{TenantAppId: , RoleCode: "admin", RoleName: "admin"},
{TenantAppId: , RoleCode: "dev", RoleName: "dev"},
{TenantAppId: , RoleCode: "plan", RoleName: "plan"},
{TenantAppId: , RoleCode: "admin", RoleName: "admin"},
{TenantAppId: , RoleCode: "plan", RoleName: "plan"},
{TenantAppId: , RoleCode: "dev", RoleName: "dev"},
} //测试该方法的功能
appInfos := GetTenantAppInfosFromAppContainers(appContainers) test.Equals(t, len(appInfos), )
test.Equals(t, len(appInfos[].Roles), )
test.Equals(t, len(appInfos[].Roles), )
test.Equals(t, len(appInfos[].Roles), )
test.Equals(t, appInfos[].Roles[].RoleCode, "admin")
test.Equals(t, appInfos[].Roles[].RoleName, "admin")
test.Equals(t, appInfos[].Roles[].RoleCode, "plan")
test.Equals(t, appInfos[].Roles[].RoleName, "plan")
test.Equals(t, appInfos[].TenantAppId, int64())
}
你们准备好应对如下的场景吗?
产品经理:因为客户的需求我们需要赶紧发布最新版本。
我:稍等,让我确认一下~cd models
go test
cd ..
cd controllers
go test
我:
没问题,现在可以发布...^^
下篇文章介绍项目中我是怎么使用测试替身的…^^
未完继续
Go项目的测试代码2(项目运用)的更多相关文章
- Go项目的测试代码1(基础)
最近写了测试代码,整理了一下. 先看看简单的测试代码. // add_test.go ==> 文件名 _test.go 结尾的默认为测试代码文件 package models import ( ...
- Go项目的测试代码3(测试替身Test Double)
上一篇文章介绍了项目中测试代码的写法. Go项目的测试代码2(项目运用) 这里简单的共享一下测试替身. 当我们写测试代码的时候,经常遇到一个问题.跟别的模块或服务有依赖性,可是功能还没开发完.或是因为 ...
- 【Android测试】【第十三节】Uiautomator——如何组织好你的测试代码(项目实战)
◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/4996000.html 前言 前面我们已经了解Uiautom ...
- 关于idea跳过错误编译的理解, 跳过报错的代码启动项目去debug测试其他正常的代码
关于idea跳过错误编译的理解 2018年07月13日 19:06:32 weixin_39669410 阅读数 1296 其实idea使用eclipse编译器可以实现跳过报错的代码启动项目去de ...
- Web项目后台测试流程
1. 本地下载项目源码 1. Git clone项目代码到本地(本地项目代码1)并fetch: 2. Switch到master分支: 3. Create测试分支(例如:test1)并勾选“Switc ...
- spring boot项目如何测试,如何部署
有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发.调试.打包到最后的投产上线. 开发阶段 单元 ...
- 单元测试系列之四:Sonar平台中项目主要指标以及代码坏味道详解
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6766994.html 众所周知Sona ...
- 使用OClint进行iOS项目的静态代码扫描
使用OClint进行iOS项目的静态代码扫描 原文链接:http://blog.yourtion.com/static-code-analysis-ios-using-oclint.html 最近需要 ...
- [福大软工] Z班 个人项目自动测试结果
个人项目第二次测试结果[9.16] 注:下表中的成绩满分为25分,正确性测试 共5个,每个3分.效率测试共 2个,每个5分. 根据数据统计分档如下, // 前为档级,后为分数. 参数为50000 0- ...
随机推荐
- MSP432 BSL流程(UART)
升级流程 PC程序会解析脚本中的命令,根据命令码做相应的操作.数据来自于命令后的文件(当前目录下的数据文件) # cat script_P4xx_uart.txt LOG //记录日志 MODE P4 ...
- 在线播放mp4
在线播放mp4 1.准备好支持再网页上播放的mp4格式 AVC(h264) 格式的mp4 2.用ckplayer控制播放 (1)到ckplayer下载js.如:http://www.ckplayer. ...
- 第一章、VUE-挂载点-实例成员-数据-过滤器-文本指令-事件指令-属性指令-表单指令-01
目录 路飞项目 vue vue 导读 vue 的优势 渐进式框架 引入 vue 实例成员 - 挂载点 el js 对象(字典)补充 实例成员 - 数据 data 实例成员 - 过滤器 filters ...
- 《浏览器工作原理与实践》<04>从输入URL到页面展示,这中间发生了什么?
“在浏览器里,从输入 URL 到页面展示,这中间发生了什么? ”这是一道经典的面试题,能比较全面地考察应聘者知识的掌握程度,其中涉及到了网络.操作系统.Web 等一系列的知识. 在面试应聘者时也必问这 ...
- PHP判断是否有Get参数的方法
PHP如何判断是否有Get参数,方法很简单,一个函数就可以搞定,需要的朋友可以参考下 if(is_array($_GET)&&count($_GET)>0)//判断是否有Get参 ...
- 【NOIP/CSP2019】D1T2 括号树
原题: 因为是NOIP题,所以首先先看特殊数据,前35分是一条长度不超过2000的链,N^2枚举所有子区间暴力check就能拿到分 其次可以思考特殊情况,一条链的情况怎么做 OI系列赛事的特殊性质分很 ...
- Zookeeper与Kafka Kafka
Zookeeper与Kafka Kafka Kafka SocketServer是基于Java NIO开发的,采用了Reactor的模式(已被大量实践证明非常高效,在Netty和Mina中广泛使用). ...
- Ubuntu 18.04实现实时显示网速
1.添加源 sudo add-apt-repository ppa:fossfreedom/indicator-sysmonitor 2.更新源 sudo apt-get update 3.安装sys ...
- 《深入理解Java虚拟机》之(三、虚拟机性能监控与故障处理工具)
一.JDK的命令行工具 1.jps:虚拟机进程状况工具 功能:可以列出正在运行的虚拟机进程,并显示虚拟机执行朱磊名称以及这些进程的本地虚拟机唯一ID. 2.jstat:虚拟机统计信息监控工具 Jsta ...
- Codeforces Round #590 (Div. 3) B2. Social Network (hard version)
链接: https://codeforces.com/contest/1234/problem/B2 题意: The only difference between easy and hard ver ...