2

I am using this go library(https://github.com/abourget/goproxy) to create a custom proxy server app. The app is utilizing several middleware of the following form:

MyMiddlewarFunc(ctx *goproxy.ProxyCtx) goproxy.Next {
    if ctx.Req.Header.Get("If-None-Match") == "test"{
       //do something
    }
}

Now, I am trying to write unit tests for these middleware functions. Here are two possible different approach I could came up with:

  1. Write test functions to call these middlware with custom made 'goproxy.ProxyCtx' instnace and inserting required dummy data in that structure. This would look something like:

    func TestMyMiddlewarFunc() { ctx = &goproxy.ProxyCtx{} ctx.Req.Header.Set("If-None-Match", "test") ctx.Req.Header.Set("If-Modified-Since", "test") MyMiddlewarFunc(ctx) //verify if changes are done according to logic }

    Pros: This approach seems to working for most of the cases, simple to write. Cons: Has one problem: as the ctx data structure is from a third party, sometimes it would be difficult to know what kind of data need to set inside it to trigger/execute something properly. For example, inside a middleware, I have a call to 'ctx.DispatchResponseHandlers' method, but it throws runtime error, most possibly because of something not set properly while passing the instance.

  2. Create a test http server, handle the middleware, and send a custom request that would trigger the mechanism inside the middleware and then verify the response/internal to see if the changes are there.

     func TestMyMiddlewarFunc() {
    
     proxyServer := goproxy.NewProxyHttpServer() 
     proxyServer.HandleFunc(MyMiddlewarFunc)   
     s := httptest.NewServer(proxyServer)
    
     client := &http.Client{}
    
     req, _ := http.NewRequest("GET", s.URL+"/foo", nil)
     req.Header.Set("Origin", "foo")
     resp, err := client.Do(req)
    
     //verify resp
     }
    

    Pros: This second approach seems to handle the problem faced on the previous approach. Cons:

    • Has little bit more overheads for setup.
    • As there are several different middleware and dependencies among them, it requires to make several different mock methods to handle those dependencies, which making it more complex structure.
    • Moreover, its kind of having integration test flavour, but though its actually suppose to be unit test.

So, I am curious to know which approach would you choose and why? Some details to support your choice would be great.

Robert Harvey
  • 200,592
Rana
  • 141

1 Answers1

1

Neither #1 nor #2 would give you unit tests, in my opinion. While there is no objective distinction between unit test and integration test, a test that covers some code you own and some code you don't own would probably be considered integration more often than not.

Since Go supports first-class functions, the simplest way to convert your example to support real unit tests would be to change this:

MyMiddlewarFunc(ctx *goproxy.ProxyCtx) goproxy.Next {
    if ctx.Req.Header.Get("If-None-Match") == "test"{
       //do something
    }
}

to something more like this:

MyMiddlewarFunc(ctx *goproxy.ProxyCtx, doSomething func()) goproxy.Next {
    if ctx.Req.Header.Get("If-None-Match") == "test"{
       doSomething();
    }
}

Now you can easily write a test on whatever function you pass in for doSomething that doesn't need to know anything about goproxy. That would probably be a real unit test (assuming doSomething() is fairly simple).

This isn't to say that the two types of integration tests you describe are undesirable. In fact, you should have some of them. But you should aim to have a lot more unit tests than integration tests, as unit tests tend to scale much better and be less brittle. As to whether your integration tests should be of type 1 or 2, I agree with Robert's comment: Use 1 when 1 works and 2 when 2 works.

Ixrec
  • 27,711