如果使用依赖注入的方式来实现 TDD,怎么避免注入冗余的参数呢? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
pkoukk
V2EX    Go 编程语言

如果使用依赖注入的方式来实现 TDD,怎么避免注入冗余的参数呢?

  •  
  •   pkoukk 2021-03-18 14:47:59 +08:00 2027 次点击
    这是一个创建于 1716 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近想学习一下 TDD,在网上找到了一篇文章 learn-go-with-tests
    中间使用了依赖注入方式来预留给测试进行 MOCK,这部分我怎么看怎么感觉别扭,如果我需要根据 if else 初始化不同的资源呢?

    func Exec(v1 int, v2 string) int { count := 0 if v1 > 10 { q := NewQuery() q.Query(v1) count += 1 } if strings.HasPrefix(v2, "a") { d := NewUpdate() d.Update(v2) count += 2 } return count } 

    如果要使用依赖注入,是要改成下面的样子么?

    func ExecDI(v1 int, v2 string, q QueryModule, d UpdateModule) int { count := 0 if v1 > 10 { q.Query(v1) count += 1 } if strings.HasPrefix(v2, "a") { d.Update(v2) count += 2 } return count } 

    那如果子模块也有资源需要初始化,那也要从最顶层传进去么?

    func ExecDIDI(v1 int, v2 string, q QueryModule, d UpdateModule, configs config.Config, db *sql.DB) int { count := 0 if v1 > 10 { q.QueryDI(v1, configs) count += 1 } if strings.HasPrefix(v2, "a") { d.UpdateDI(v2, db) count += 2 } return count } 

    假设一个偏远的代码分支需要从 Http 获取一些数据,本来只要执行到分支这里的时候,再让它自行去获取就好。
    那改成依赖注入会变成从一开始就要获取么?
    如果还是保留这部分不注入,那么这部分代码块就没法 mock 以进行充分的测试了

    9 条回复    2021-03-18 16:28:00 +08:00
    anonydmer
        1
    anonydmer  
       2021-03-18 14:56:04 +08:00
    ```
    type Executor struct {
    q QueryModel
    d UpdateModel
    }

    func (e Executor) Exec(v1 int, v2 string) int{
    count := 0
    if v1 > 10 {
    e.q.Query(v1)
    count += 1
    }
    if strings.HasPrefix(v2, "a") {
    e.d.Update(v2)
    count += 2
    }
    return count
    }
    ```
    pkoukk
        2
    pkoukk  
    OP
       2021-03-18 15:06:51 +08:00
    @anonydmer
    这还是不能解决问题啊,QueryModel 和 UpdateModel 在哪里初始化?初始化时候所需的资源从哪里来?
    我上面的示例只是一个简化的模式,实际上 QueryModel 和 UpdateModel 也可能包含的还有子模块,子模块也需要初始化
    anonydmer
        3
    anonydmer  
       2021-03-18 15:15:13 +08:00
    1. 对于这个单测来说,他依赖的只是 QueryModel 的 Query 方法;如果 QueryModel 是个接口的话,你随便注入一个实现他的就可以
    2. 你这个测试如果还 QueryModel 的子模块就不对了;
    3. QueryModel 如何初始化那是另外的事情,一般是在 Executor 实例化时初始化注入( Golang 中推荐的实际是一个 struct 使用一个工厂方法来实例化);单测时候直接注入 mock 的
    anonydmer
        4
    anonydmer  
       2021-03-18 15:16:06 +08:00
    上述 2, “你这个测试如果还 QueryModel 的子模块就不对了” -> "你这个测试如果还依赖 QueryModel 的子模块就不对了"
    carlclone
        5
    carlclone  
       2021-03-18 15:21:20 +08:00
    你需要一个 Service 层或者 Repository 层, 然后把他们注入进来
    zjsxwc
        6
    zjsxwc  
       2021-03-18 15:31:07 +08:00
    参数注入本来就会碰到楼主说的这种问题。

    所以我们都用依赖注入容器,让容器来管理。

    楼主这种“如果子模块也有资源需要初始化,那也要从最顶层传进去么?”问题,
    只需要在容器里搞个原资源类似别名的新资源代替原资源就行。


    推荐个刚刚看到的 golang 依赖注入容器
    https://github.com/bassbeaver/gioc
    pkoukk
        7
    pkoukk  
    OP
       2021-03-18 16:05:36 +08:00
    @anonydmer
    这只是一个简化模型...我知道你的方式可以完成测试,但是如果面对更复杂的状况呢?
    假设在第一个分支里,query 返回 a,这个 a 的返回是依赖于 query 子模块进行的一些 http 操作而来的,a 会被用作 exec 函数下面的分支里进行判断,那么如果不能对 query 的子模块进行 mock,就无法控制 a 的返回,那么 exec 的测试就无法全覆盖。

    我想表达的是冲突就在于如果需要注入依赖,那么是不是就得全注进去?如果不是全部注入,就意味着子模块有独立的数据源,不受上层模块的控制。如果是全部注入,那么问题就是提前无法确认哪些资源会被用到。

    5,6 楼的方法应该可行..但是就感觉本来简单的结构变得太复杂了
    anonydmer
        8
    anonydmer  
       2021-03-18 16:19:57 +08:00
    “假设在第一个分支里,query 返回 a,这个 a 的返回是依赖于 query 子模块进行的一些 http 操作而来的,a 会被用作 exec 函数下面的分支里进行判断,那么如果不能对 query 的子模块进行 mock,就无法控制 a 的返回,那么 exec 的测试就无法全覆盖”

    这时候你应该在多个测试用例中直接 mock query 返回不同的 a ;
    pkoukk
        9
    pkoukk  
    OP
       2021-03-18 16:28:00 +08:00
    @anonydmer
    你说的对...我把自己绕进去了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     826 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 20:20 PVG 04:20 LAX 12:20 JFK 15:20
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86