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.

  • 3
    Why not use option 1 for most of your tests, and option 2 only for the tests that have the ctx data structure problem? Your question seems to imply that you would use only one approach. – Robert Harvey Dec 4 '15 at 6:34
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.

  • On thing: its not possible to change the function format for the middleware. That's getting called from third party(goproxy) and its really how goproxy expects it to be like(with 1 ctx argument). So, not saying for this specific domain, it might be happen in other cases, where its not possible to just change the function signature, then what would be your approach? – Rana Dec 4 '15 at 16:07
  • @Rana Are you saying you don't own MyMiddlewarFunc, or you do but you can't add arguments to it? If the latter, you can probably still get the doSomething() out of there with a little more effort, probably via closures or currying or some other functional trick. If the former, then I'd say that in general you don't want to be unit testing code you don't own even if you are able to, so sticking to integration tests is fine. – Ixrec Dec 4 '15 at 19:19
  • I do own that function. But, the fact its: its a middleware I am writing which is injected by the third party library. Its like a plugin for a platform, where you will have to write your plugin method in some specific format and only then it get executed the the platform. Did I able to make it clear? – Rana Dec 4 '15 at 19:28
  • I think so. I'll edit my answer at some point to use a closure instead of a second argument so it fits that requirement. – Ixrec Dec 4 '15 at 19:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.