简体   繁体   中英

Dependency injection in golang

I have previous code which works like following:

  1. call to function getData which provide you data to execute the http request
  2. use the getData function output to execute the http request.

This is works before, but now I want to make unit test for it, and I read some blogs how to make this work and it seems that the dependency injection is the key to do it. I tried to follow this post .

I try to adopt the code but not sure if I did it right.

The idea is to use getData function in prod with some url to execute and in the unit test provide diffrent url like from httptest.NewServer(testHandler) How should I make it right in 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()
    }

The test looks something like this but really not sure how to make it work

    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())

                })

The code is just and example to my complete code so I try take it and put only the relevant parts

update to make it more clear :)

What I need is how to mock (right way) the function getData , to provide url x in prod code and url y in test, the nuance here that I need to make a unit test to the function caller

To make it work this is not an issue but the issue here to make it testable code

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

I've been using https://github.com/facebookgo/inject for DI and https://github.com/golang/mock for mocking. For example:

// 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
}

And generate mock implementation using:

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

Then use this mock implementation in writing unit tests.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM