具有依赖项注入的测试包装功能

时间:2018-12-20 17:04:45

标签: go dependency-injection

我有这个功能需要在测试中进行模拟, 我可以使用http模拟程序包进行模拟,但是现在我正在调用 对于HttpReq方法,在这里我不能使用http模拟包 我阅读了有关依赖项注入的内容,并尝试了一些方法,但我无法完全做到这一点,

这是功能

type params struct {
    cs     string
    ci     string
    method string
    url    string
}

// I added this struct but not sure if it's needed ... probably for test purpose but not sure how to use it.

type Impl struct {
 client *http.Client
}

func (i *Impl) HttpReq(p *params) ([]byte, error) {
    httpClient := i.client
    req, err := http.NewRequest(p.method, p.url, nil)
    if err != nil {
        fmt.Sprintf(err)
    }
    req.SetBasicAuth(p.cs, p.ci)
    res, err := httpClient.Do(req)
    if err != nil {
        fmt.Sprintf(err)
    }
    t, err := ioutil.ReadAll(res.Body)
    if err != nil {
        fmt.Sprintf(err)
    }
    defer res.Body.Close()
    return t, nil
}

这是我尝试过的

我已经创建了界面

type Req interface {
    HttpReq(params) ([]byte, error)
}

现在,我创建了一个包含接口的结构

type Service struct {
    req Req
}

这是我需要测试的功能

func (c *Service) execute(cli Connection , args []string) (error, []byte) {
    sk, err := c.doSomthing(cli, args)

    sc, err := c.doSometing2(serviceK, []string{"url", "cl", "ct"})

    cc := strings.Fields(serviceCredentials)

    // ----------Here is what I need to mock ----------
    t, err := c.req.HttpReq(params{cs: cc[1],
        ci:     cc[2],
        method: http.MethodPost,
        url:    cc[0],})
    return err, t
}

任何想法我如何可以对此功能进行测试???我为此苦苦挣扎。

2 个答案:

答案 0 :(得分:2)

与原始问题无关,您不应为每个请求创建新的HTTP客户端。客户端维护一个连接池,应尽可能重用。

您可以修复该问题,并通过注入HTTP客户端来继续使用现有的模拟服务器。

还请注意,问题中的接口定义与实现不匹配。这两个方法签名不相同:

HttpReq(params) ([]byte, error)  // Req
HttpReq(*params) ([]byte, error) // Impl

选择一个。我可能会在这里使用非指针类型。 And upper case initials are idiomatic in GoHTTPReq,而不是HttpReq)。

将客户端添加到Impl类型,并在HTTPReq中使用它:

type Impl struct {
    client *http.Client
}

func (i *Impl) HTTPReq(p params) ([]byte, error) {
    req, err := http.NewRequest(p.method, p.url, nil)
    if err != nil {
        return nil, err
    }
    req.SetBasicAuth(p.cs, p.ci)

    res, err := i.client.Do(req)
    if err != nil {
        return nil, err
    }
    defer res.Body.Close()

    return ioutil.ReadAll(res.Body)
}

服务类型不必更改。

在测试中,只需将测试客户端插入Impl值:

import (
    "context"
    "net"
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestService_execute(t *testing.T) {
    var testHandler http.Handler // TODO

    srv := httptest.NewServer(testHandler)
    defer srv.Close()

    client := srv.Client()
    tp := client.Transport.(*http.Transport)

    // Direct all requests to the test server, no matter what the request URL is.
    tp.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
        // Note that we ignore the network and addr parameters, since these are 
        // derived from the request URL and are unrelated to the test server.

        srvAddr := srv.Listener.Addr()
        return (&net.Dialer{}).DialContext(ctx, srvAddr.Network(), srvAddr.String())
    }

    svc := &Service{
        req: &Impl{
            client: client,
        },
    }

    svc.execute(/* ... */)

    // assert request, response, etc.
}

答案 1 :(得分:1)

由于Service结构已经具有req接口,因此在测试过程中,使用满足req接口的模拟初始化服务对象。 这样的东西
https://stackoverflow.com/a/53805535/3968921