douyong1885 2018-12-20 17:04
浏览 13
已采纳

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

I have this function which I need to mock in test, I was able to mock it as expected with http mock package , but now I’ve function that are calling To the HttpReq method and here I cannot use http mock package I read about dependency injection and tried something but I wasn’t able to fully do it,

This is the function

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
}

This is what I tried

I’ve created interface

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

Now I’ve created a struct which contain the interface

type Service struct {
    req Req
}

This is the function which I need to tests

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
}

Any idea how I can run test for this function ??? Im struggling with it a lot.

  • 写回答

2条回答 默认 最新

  • douwu7168 2018-12-20 17:40
    关注

    Independent of the original question, you should not create new HTTP clients for each request. Client's maintain a connection pool and should be reused as much as possible.

    You can fix that, and continue using your existing mock server by injecting the HTTP client.

    Note also that the interface definition in the question doesn't match the implementation. These two method signatures are not the same:

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

    Pick one. I would probably go with the non-pointer type here. And upper case initials are idiomatic in Go (HTTPReq, not HttpReq).

    Add the client to the Impl type and use it in 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)
    }
    

    The Service type doesn't have to change.

    In the tests, simply inject a test client into the Impl value:

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

报告相同问题?

悬赏问题

  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码