go - 依赖注入(inject)和测试

标签 go

我正在开发一个小型 Go 应用程序,它基本上是各种密码存储(Ansible Vault、Hashicorp Vault、Chef Vault 等)的包装器。这个想法是:在我的各种配置脚本中,我可以使用我的 Go 包装器来获取 secret ,如果我们决定在幕后切换密码存储,则不需要在我的项目中更新所有接口(interface)。

我正在尝试为此应用程序设置适当的测试,并在此过程中尝试找出注入(inject)依赖项的最佳方法。

例如,假设该项目名为 secrets。我的实现之一是 ansible。 ansible 实现需要自己的解析器,并且需要打开自己的连接到 ansibleVault,以检索数据。

所以我可能有以下内容:

package secrets

type PasswordStore interface {
    GetKey(key string) (string, error)
}

func New(backend string, config map[string]interface{}) (PasswordStore, error) {
    switch backend {
    case "ansible":
        return ansible.New(config)
    default:
        return nil, fmt.Errorf("Password store '%s' not supported.", backend)
    }
}


package ansible


type Connection interface {
    open() (string, error)
}

type Ansible struct {
    connection Connection
    contents   map[string]string
}

func New(c map[string]interface{}) (*Ansible, error) {
    conn, err := NewConnection(c["ansible_path"].(string))
    if err != nil {
        return nil, err
    }

    // open connection, parse, etc...   

    a := &Ansible{
        connection: conn,
        contents:   parsedData,
    }

    return a, nil
}

所以这看起来不错,因为 secrets 包不需要了解 ansible 包依赖项(连接),并且工厂刚刚使用一些配置新建了实例数据。但是,如果我需要模拟 Ansible 接收的 connection ,似乎没有一个好的方法来做到这一点(除非该配置映射有一个名为 mock 的连接选项>)

另一个选择是放弃工厂,只组装 secrets 包中的所有依赖项,例如:

package secrets

type PasswordStore interface {
    GetKey(key string) (string, error)
}

func New(backend string, config map[string]interface{}) (PasswordStore, error) {
    switch backend {
    case "ansible":
        return ansible.New(AnsibleConnection{}, config)
    default:
        return nil, fmt.Errorf("Password store '%s' not supported.", backend)
    }
}

package ansible


// same as before in this file, but with injected dependency ...

func New(connect Connection, c map[string]interface{}) (*Ansible, error) {
    conn, err := connect.NewConnection(c["ansible_path"].(string))
    if err != nil {
        return nil, err
    }

    // open connection, parse, etc...   

    a := &Ansible{
        connection: conn,
        contents:   parsedData,
    }

    return a, nil
}

现在依赖项已注入(inject),但似乎 secrets 需要了解每个实现的每个依赖项。

是否有更合乎逻辑的方式来构建这个,以便 secrets 知道得更少?或者顶层包通常会协调一切?

最佳答案

什么决定了后端是什么?这应该有助于指导你。我在一个项目中做过类似的支持多个数据库的事情,我所做的基本上是:

  • config 包读取配置文件,该文件确定正在使用的后端
  • store 包提供通用接口(interface),并具有一个接受配置并返回实现的函数
  • server 包仅引用接口(interface)
  • main 包读取配置,将其传递给 store 中的工厂函数,然后在创建时将结果注入(inject)到服务器

因此,当我创建服务器(实际上使用数据存储)时,我将配置传递给 store 中的工厂函数,该函数返回一个接口(interface),然后将其注入(inject)到服务器中。关于不同的具体实现,唯一需要了解的是公开接口(interface)和工厂的同一个包; serverconfigmain 包将其视为黑匣子。

关于go - 依赖注入(inject)和测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45764315/

相关文章:

go - 在 Go 中将图像转换为灰度

http - 输入 TYPE TEXT 值形式 (enctype =“multipart/form-data” ) 返回 null

go - 将函数的值作为输入参数返回给另一个

go - 使用其他文件的功能是其他目录Golang

go - 包中结构的惯用 Go 名称?

Golang 将结构分配给另一个结构字段不起作用

go - 尝试在 golang 中解码 gob 时出现 "extra data in buffer"错误

go - 根据名称在 JSON Schema 中搜索 JSON 对象并获取路径

go - Elasticsearch + 去 : Index failures (No feature for name)

select - 选择中同时发生多个事件时的预期行为