首页 > 其他分享 >搞定Go单元测试(四)—— 依赖注入框架(wire)

搞定Go单元测试(四)—— 依赖注入框架(wire)

时间:2023-03-07 10:13:30浏览次数:46  
标签:wire 函数 mysql 单元测试 injector 依赖 provider Go

在第一篇文章中提到过,为了让代码可测,需要用依赖注入的方式来构建我们的对象,而通常我们会在main.go做依赖注入,这就导致main.go会越来越臃肿。为了让单元测试得以顺利进行,main.go牺牲了它本应该纤细苗条的身材。太胖的main.go可不是什么好的信号,本篇将介绍依赖注入框架(wire),致力于帮助main.go恢复身材。

臃肿的main

main.go中做依赖注入,意味着在初始化代码中我们要管理:

  1. 依赖的初始化顺序
  2. 依赖之间的关系

对于小型项目而言,依赖的数量比较少,初始化代码不会很多,不需要引入依赖注入框架。但对于依赖较多的中大型项目,初始化代码又臭又长,可读性和维护性变的很差,随意感受一下:

func main() {
    config := NewConfig()
    // db依赖配置
    db, err := ConnectDatabase(config) 
    if err != nil {
        panic(err)
    }
    // PersonRepository 依赖db
    personRepository := NewPersonRepository(db) 
    // PersonService 依赖配置 和 PersonRepository
    personService := NewPersonService(config, personRepository)
    // NewServer 依赖配置和PersonService
    server := NewServer(config, personService)
    server.Run()
}
复制代码

实践表明,修改有大量依赖关系的初始化代码是一项乏味且耗时的工作。这个时候,我们就需要依赖注入框架来帮忙,简化初始化代码。

上述代码来自:blog.drewolson.org/dependency-…

使用依赖注入框架——wire

What is wire?

wire是google开源的依赖注入框架。或者引用官方的话来说:“Wire is a code generation tool that automates connecting components using dependency injection”。

github.com/google/wire

Why wire?

除了wire,Go的依赖注入框架还有Uber的dig和Facebook的inject,它们都是使用反射机制来实现运行时依赖注入(runtime dependency injection),而wire则是采用代码生成的方式来达到编译时依赖注入(compile-time dependency injection)。使用反射带来的性能损失倒是其次,更重要的是反射使得代码难以追踪和调试(反射会令Ctrl+左键失效...)。而wire生成的代码是符合程序员常规使用习惯的代码,十分容易理解和调试。
关于wire的优点,在官方博文上有更详细的的介绍:blog.golang.org/wire

How does it work?

本部分内容参考官方博文:blog.golang.org/wire

wire有两个基本的概念:provider和injector。

provider

provider就是普通的Go函数,可以把它看作是某对象的构造函数,我们通过provider告诉wire该对象的依赖情况:

// NewUserStore是*UserStore的provider,表明*UserStore依赖于*Config和 *mysql.DB.
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}

// NewDefaultConfig是*Config的provider,没有依赖
func NewDefaultConfig() *Config {...}

// NewDB是*mysql.DB的provider,依赖于ConnectionInfo
func NewDB(info ConnectionInfo) (*mysql.DB, error) {...}

// UserStoreSet 可选项,可以使用wire.NewSet将通常会一起使用的依赖组合起来。
var UserStoreSet = wire.NewSet(NewUserStore, NewDefaultConfig)
复制代码

injector

injector是wire生成的函数,我们通过调用injector来获取我们所需的对象或值,injector会按照依赖关系,按顺序调用provider函数:

// File: wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject

// initUserStore是由wire生成的injector
func initUserStore(info ConnectionInfo) (*UserStore, error) {
    // *Config的provider函数
    defaultConfig := NewDefaultConfig()
    // *mysql.DB的provider函数
    db, err := NewDB(info)
    if err != nil {
        return nil, err
    }
    // *UserStore的provider函数
    userStore, err := NewUserStore(defaultConfig, db)
    if err != nil {
        return nil, err
    }
    return userStore, nil
}
复制代码

injector帮我们把按顺序初始化依赖的步骤给做了,我们在main.go中只需要调用initUserStore方法就能得到我们想要的对象了。

那么wire是怎么知道如何生成injector的呢?我们需要写一个函数来告诉它:

  • 定义injector的函数签名
  • 在函数中使用wire.Build方法列举生成injector所需的provider

例如:

// initUserStore用于声明injector的函数签名
func initUserStore(info ConnectionInfo) (*UserStore, error) {  
    // wire.Build声明要获取一个UserStore需要调用到哪些provider函数
    wire.Build(UserStoreSet, NewDB)
    return nil, nil  // 这些返回值wire并不关心。
}
复制代码

有了上面的函数,wire就可以得知如何生成injector了。wire生成injector的步骤描述如下:

  1. 确定所生成injector函数的函数签名:func initUserStore(info ConnectionInfo) (*UserStore, error)
  2. 感知返回值第一个参数是*UserStore
  3. 检查wire.Build列表,找到*UserStore的provider:NewUserStore
  4. 由函数签名func NewUserStore(cfg *Config, db *mysql.DB)得知NewUserStore依赖于*Config, 和*mysql.DB
  5. 检查wire.Build列表,找到*Config*mysql.DB的provider:NewDefaultConfigNewDB
  6. 由函数签名func NewDefaultConfig() *Config得知*Config没有其他依赖了。
  7. 由函数签名func NewDB(info *ConnectionInfo) (*mysql.DB, error)得知*mysql.DB依赖于ConnectionInfo
  8. 检查wire.Build列表,找不到ConnectionInfo的provider,但在injector函数签名中发现匹配的入参类型,直接使用该参数作为NewDB的入参。
  9. 感知返回值第二个参数是error
  10. ....
  11. 按依赖关系,按顺序调用provider函数,拼装injector函数。

举个栗子

栗子传送门:wire-examples

注意

截止本文发布前,官方表明wire的项目状态是alpha,还不适合到生产环境,API存在变化的可能。
虽然是alpha,但其主要作用是为我们生成依赖注入代码,其生成的代码十分通俗易懂,在做好版本控制的前提下,即使是API发生变化,也不会对生成环境造成多坏的影响。我认为还是可以放心使用的。

总结一下

本篇是本系列的最后一篇,回顾前几篇文章,我们以单元测试的原理与基本思想为基础,介绍了表格驱动测试方法,gomock,testify,wire这几样实用工具,经历了“能写单元测试”到“写好单元测试”不断优化的过程。希望本系列文章能让你有所收获。

转载自: https://juejin.im/post/5ce935dcf265da1ba431c998

标签:wire,函数,mysql,单元测试,injector,依赖,provider,Go
From: https://www.cnblogs.com/gongxianjin/p/17187072.html

相关文章

  • 搞定Go单元测试(三)—— 断言(testify
    在上一篇,介绍了表格驱动测试方法和gomock测试框架,大大提升了测试效率与质量。本篇将介绍在测试中引入断言(assertion),进一步提升测试效率与质量。为什么需要断言库我们先来......
  • 【MongoDB】移除复制集成员
    1.使用rs.remove()(1)关闭mongodb实例先关闭要移除的mongodb实例db.shutdownServer()(2)连接到复制集的primary节点使用db.hello()查看复制集的信息>db.hello(){......
  • golang 升级 1.16.3 之后,编译报错 missing go.sum entry for module providing packag
    问题现象在开发机上升级到了最新golang1.16.3版本,在为一个基于golang1.13的历史项目添加excel依赖包后gogetgithub.com/360EntSecGroup-Skylar/excelize/v2......
  • google独立站哪家做得好?
    google独立站哪家做得好?答案是:光算科技做外贸独立站很棒。谷歌是全球最受欢迎的搜索引擎之一,而Google独立站则是许多企业和网站所采用的一种高效的网络营销工具。Google独立......
  • Google独立站和与企业官网的区别是什么?
    google独立站和与企业官网的区别是什么?答案是:独立站通过谷歌SEO优化可以更好的获取自然排名的流量。随着互联网的不断发展,企业越来越重视自身网站的建设和优化,而在企业网站......
  • 2023年如何在Google做外贸
    2023年如何在Google做外贸答案是:利用谷歌SEO获取自然流量促进成交。随着全球化和数字化的发展,外贸行业越来越重视互联网的渠道拓展。在Google搜索引擎上做好SEO优化,是吸引国......
  • Google Review评价如何影响搜索排名?是否有助于提升搜索曝光量
    2015年,全球搜索巨头——Google谷歌推出了我的商家GoogleMyBusiness(GMB),并效仿Yelp、Tripadvisor等点评平台,打造属于商家点评生态体系,挖掘更多本地搜索业务的潜力,并在美国......
  • 为什么 Go 语言是后端开发的未来
    近年来,Go语言的流行度迅速增加。Go最初由Google开发,已迅速成为最流行的后端开发语言之一,尤其是在分布式系统和微服务开发方面。在本文中,我们将讨论Go是后端开发未来......
  • PowerShell批量logoff VDI用户
    下一个脚本也是非常简单实用的,和之前的脚本一起配合效果会非常好,上一个脚本可以在变更之前批量给用户发通知,这个脚本就是变更开始时,直接把用户logoff,这样再执行变更会好一......
  • 【django-vue】登录注册模态框分析 登录注册前端页面 腾讯短信功能二次封装 短信验证
    目录昨日回顾csrf跨站请求伪造接口幂等性异常捕获今日内容1登录注册模态框分析Login.vueHeader.vue2登录注册前端页面复制2.0Header.vue2.1Login.vue2.2Register.vue3......