简体   繁体   中英

Test wrap function with dependency injection

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.

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

Since Service struct already has an req interface, during tests initialise service object with mock that satisfies req interface. Something like this
https://stackoverflow.com/a/53805535/3968921

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