1

As part of my first project I am creating a tiny library to send an SMS to any user. I have added the logic of waiting and retrying if it doesn't receive a positive status on first go. It's a basic HTTP call to am SMS sending service. My algorithm looks like this (comments would explain the flow of the code):

for {
    //send request
    resp, err := HTTPClient.Do(req)
    checkOK, checkSuccessUrl, checkErr := CheckSuccessStatus(resp, err)

    //if successful don't continue
    if !checkOK and checkErr != nil {
        err = checkErr
        return resp, SUCCESS, int8(RetryMax-remain+1), err
    }

    remain := remain - 1
    if remain == 0 {
        break
    }

    //calculate wait time
    wait := Backoff(RetryWaitMin, RetryWaitMax, RetryMax-remain, resp)
    //wait for time calculated in backoff above
    time.Sleep(wait)

    //check the status of last call, if unsuccessful then continue the loop

    if checkSuccessUrl != "" {
        req, err := GetNotificationStatusCheckRequest(checkSuccessUrl)
        resp, err := HTTPClient.Do(req)
        checkOK, _, checkErr = CheckSuccessStatusBeforeRetry(resp, err)
        if !checkOK {

            if checkErr != nil {
                err = checkErr
            }
            return resp,SUCCESS, int8(RetryMax-remain), err
        }
    }
}

Now I want to test this logic using any HTTP mock framework available. The best I've got is https://github.com/jarcoal/httpmock

But this one does not provide functionality to mock the response of first and second URL separately. Hence I cannot test the success in second or third retry. I can either test success in first go or failure altogether.

Is there a package out there which suits my needs of testing this particular feature? If no, How can I achieve this using current tools?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
diwakarb
  • 543
  • 2
  • 9
  • 23
  • Instead of Mocking the http requests you could define a minimal interface that returns the data you expect from the http call or an error. Then you can make a test implementation that fails a few times before succeeding. – Thomas Apr 28 '18 at 21:40
  • I have kept this as a last resort. Because, I might have to extend/use my current class with the said minimal interface and that will change the current structure of my code. Doesn't go have a mature mocking framework for these purposes ? Something like what mockito have in Java: https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#stubbing_consecutive_calls – diwakarb Apr 28 '18 at 22:13
  • I've used the mock package from https://github.com/stretchr/testify and http://github.com/vektra/mockery to generate the mock code. Not sure if it has the feature you want. I found it cumbersome to work with. I think generally mocks are not considered a great practice in go and using a simple interface and test implementation are more idiomatic. YMMV of course. – Thomas Apr 28 '18 at 22:28
  • 2
    "But this one does not provide functionality to mock the response of first and second url separately. " sure it does, you can pass a function with custom logic, as explained in advanced example on the page you linked. – mpm Apr 28 '18 at 23:18
  • It looks like it'll work, I'll give it a try. Thanks for suggesting. – diwakarb Apr 29 '18 at 00:37

2 Answers2

3

This can easily be achieved using the test server that comes in the standard library's httptest package. With a slight modification to the example contained within it you can set up functions for each of the responses you want up front by doing this:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httptest"
)

func main() {
    responseCounter := 0
    responses := []func(w http.ResponseWriter, r *http.Request){
        func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintln(w, "First response")
        },
        func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintln(w, "Second response")
        },
    }
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        responses[responseCounter](w, r)
        responseCounter++
    }))
    defer ts.Close()

    printBody(ts.URL)
    printBody(ts.URL)
}

func printBody(url string) {
    res, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    resBody, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%s", resBody)
}

Which outputs:

First response
Second response

Executable code here:

https://play.golang.org/p/YcPe5hOSxlZ

Iain Duncan
  • 3,139
  • 2
  • 17
  • 28
0

Not sure you still need an answer, but github.com/jarcoal/httpmock provides a way to do this using ResponderFromMultipleResponses.