为了账号安全,请及时绑定邮箱和手机立即绑定

使用依赖注入测试包装函数

使用依赖注入测试包装函数

Go
智慧大石 2023-05-22 15:50:09
我有这个函数,我需要在测试中模拟,我能够用 http mock package 模拟它,但现在我有调用方法的函数,在这里我不能使用 http HttpReqmock package 我读过关于依赖注入并尝试了一些但我无法完全做到,这是功能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 回答

?
Cats萌萌

TA贡献1805条经验 获得超9个赞

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

您可以解决这个问题,并通过注入 HTTP 客户端继续使用现有的模拟服务器。

另请注意,问题中的接口定义与实现不匹配。这两个方法签名是不一样的:

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

选一个。我可能会在这里使用非指针类型。大写首字母在 Go 中是惯用的(HTTPReq, not 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.

}


查看完整回答
反对 回复 2023-05-22
?
宝慕林4294392

TA贡献2021条经验 获得超8个赞

由于 Service 结构已经有一个 req 接口,在测试期间使用满足 req 接口的 mock 初始化服务对象。

查看完整回答
反对 回复 2023-05-22
  • 2 回答
  • 0 关注
  • 124 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信