golang中的依赖注入

时间:2019-01-18 22:16:35

标签: go dependency-injection

我有以前的代码,其工作方式如下:

  
      
  1. 调用函数getData,该函数为您提供执行http请求的数据
  2.   
  3. 使用getData函数输出执行http请求。
  4.   

这是以前可行的方法,但是现在我想对其进行单元测试,并且我读了一些博客以了解如何实现此功能,并且依赖注入似乎是实现此目的的关键。我试图关注this post

我尝试采用该代码,但不确定我是否做对了。

想法是在产品中使用getData函数并执行一些url,并在unit test中提供与httptest.NewServer(testHandler)类似的不同url 如何在Go中正确设置?

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

type requester interface {
    HTTPRequest(c string, i string, mtd string, url string) (p []byte, e error)
}
func (i impl) HTTPRequest(c string, ci string, mtd string, url string) (p []byte, e error) {
    req, err := http.NewRequest(mtd, url, nil)
    if err != nil {
        return nil, err
    }
    req.SetBasicAuth(c, ci)
    res, err := i.client.Do(req)
    if err != nil {
        return nil, err
    }
    token, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return nil, err
    }
    defer res.Body.Close()
    fmt.Println("success")
    return token, nil
}

type iData interface {
    getData() []string
}

type reqData struct {
    f1 string
    f2 string
}

func (s reqData) getData() []string {
    a := make([]string, 4)
    a[2] = "http://www.mocky.io/v2/5c20eccc2e00005c001e0c84"
    a[3] = "/oauth/token?grant_type=client_credentials"
    return a
}

type ServiceInfo struct {
    req  requester
    data iData
}

type impl struct {
    client *http.Client
}

func NewServiceInfo(http requester, data iData) *ServiceInfo {
    return &ServiceInfo{
        req:  http,
        data: data,
    }
}

// ----This is the function which I need to mock
func (s *ServiceInfo) caller() {
    // Function 1 - get the values
    reqdata := s.data.getData()
    // Function 2 -call to http function
    s.req.HTTPRequest(reqdata[0], reqdata[1], "POST", reqdata[2])
}

    func main() {
// not sure how to run it 
        //httpClient := http.Client{}
        //d := reqData{f1: "user", f2: "password"}

        //s := NewServiceInfo(impl{client: &httpClient}, d.getData())
        //s.caller()
    }

测试看起来像这样,但是真的不确定如何使它工作

    It("Test", func() {

                    // Mock http call
                    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                        u, p, ok := r.BasicAuth()
                        Ω(ok).Should(Equal(true))
                        Ω(u).Should(Equal("user"))
                        Ω(p).Should(Equal("password"))
                    }))

                    var httpClient = http.Client{}
                    si := NewServiceInfo(client{httpClient: &httpClient, url: server.URL})
                    t, err := si.r.httpReq("user", "password", http.MethodPost)

                    Ω(token).Should(Equal(string(t)))
                    Ω(err).ShouldNot(HaveOccurred())

                })

该代码只是我完整代码的示例,因此我尝试使用它并仅放入相关部分

更新 使其更加清晰:)

我需要的是如何模拟(正确的方法)功能getData, 提供产品代码中的url x和测试中的url y,这里我需要对功能caller进行单元测试的细微差别

要使其起作用,这不是问题,但要使其成为可测试代码

https://play.golang.org/p/UQqtZmNS5BK

1 个答案:

答案 0 :(得分:0)

我一直在使用https://github.com/facebookgo/inject进行DI和https://github.com/golang/mock进行模拟。 例如:

// some_entity_dao.go
type SomeEntity interface {
    Find(ctx context.Context, condition *model.SomeEntity) (*model.SomeEntity, error)
    FindAll(ctx context.Context, condition *model.SomeEntity) ([]*model.SomeEntity, error)
    Save(ctx context.Context, data *model.SomeEntity) error
    Update(ctx context.Context, condition *model.SomeEntity, data *model.SomeEntity) error
    Delete(ctx context.Context, condition *model.SomeEntity) error
}

并使用以下命令生成模拟实现:

//go:generate mockgen -source=./some_entity_dao.go -destination=./some_entity_dao_mock_impl.go -package dao

然后在编写单元测试时使用此模拟实现。