0

I'm working on testing an API server SDK written in Golang. My literal job on this is solely unit-testing, documenting the code under test, and of course reporting any fails with the rest of the dev team.

There's a few methods under test that hit other methods under test, that may hit others, that I'm responsible for testing. For example, at the time of me writing this, I'm trying to test this method:

//SendRequest This is used to contact the apiserver synchronously.
func (apiPath *APIPath) SendRequest(context interface{}, tokenHandler *apiToken.APITokenHandlerSt,
    header map[string]string,
    urlParams []string, urlQueries url.Values,
    jsonBody []byte) apiCore.CallResultSt {
    if apiToken := tokenHandler.GetToken(apiPath.AuthType, apiPath.Scope); apiToken != nil {
        return apiPath.APICoreHandler.SendRequest(
            context,
            apiToken.Token,
            apiPath.GetPath(urlParams, urlQueries), apiPath.Type,
            header, jsonBody)
    } else {
        errMsg, _ := json.Marshal(errors.InvalidAuthentication())
        return apiCore.CallResultSt{DetailObject: errMsg, IsSucceeded: false}
    }
}

where the receiver struct is defined to be

//APIPath=======================
//Used for url construction
type APIPath struct {
    APICoreHandler *apiCore.APICoreSt
    // domain name of API
    DomainPath string
    ParentAPI  *APIPath
    Type       apiCore.APIType
    // subfunction name
    SubFunc          string
    KeyName          string
    AutoAddKeyToPath bool
    AuthType         oAuth2.OAuth2Type
    Scope            string
}

Note that method has an external dependency. It is defined thus:

//Establish the request to send to the server
func (a *APICoreSt) SendRequest(context interface{}, token string, apiURL string, callType APIType, header map[string]string, jsonBody []byte) CallResultSt {
    if header == nil {
        header = make(map[string]string)
    }
    if header["Authorization"] == "" {
        header["Authorization"] = "Bearer " + token
    }
    header["Scope"] = GeneralScope
    header["Content-Type"] = "application/json; charset=UTF-8"
    return a.CallServer(context, callType, apiURL, header, jsonBody)
}

and that method ends up hitting this one:

//CallServer Calls the server
//
// Parameters:
// - `context` : a context to pass to the server
// - `apiType` : the HTTP method (`GET`,`POST`,`PUT`,`DELETE`,...)
// - `apiURL` : the URL to hit
// - `header` : request header
// - `jsonBody`: the JSON body to send
//
// Returns:
// - a CallResultSt. This CallResultSt might have an error for its `DetailObject`
func (a *APICoreSt) CallServer(context interface{}, apiType APIType, apiURL string, header map[string]string, jsonBody []byte) CallResultSt {

    var (
        Url     = a.BaseURL + apiURL
        err     error
        res     *http.Response
        resBody json.RawMessage
        hc      = &http.Client{}
        req     = new(http.Request)
    )

    req, err = http.NewRequest(string(apiType), Url, bytes.NewBuffer(jsonBody))
    if err != nil {
        //Use a map instead of errorSt so that it doesn't create a heavy dependency.
        errorSt := map[string]string{
            "Code":    "ez020300007",
            "Message": "The request failed to be created.",
        }
        logger.Instance.LogError(err.Error())
        err, _ := json.Marshal(errorSt)
        return CallResultSt{DetailObject: err, IsSucceeded: false}
    }

    for k, v := range header {
        req.Header.Set(k, v)
    }

    res, err = hc.Do(req)
    if res != nil {
        resBody, err = ioutil.ReadAll(res.Body)
        res.Body = ioutil.NopCloser(bytes.NewBuffer(resBody))
    }
    return CallResultSt{resBody, logger.Instance.CheckAndHandleErr(context, res)}

}

(The struct these two methods are on is omitted for brevity: there's only one field of that struct that it relies on.)

These type of situations keep popping up in the code under test, and yesterday, I confronted the boss about this, citing these dependencies, and the fact that there's no struct makes them literally impossible to test-double (fake,stub,mock). I was merely asking to refactor it so that it hits an interface method, declared in the same file as the dependency, which those struct methods would implement (implementing interfaces is implicit in Golang). It turned into a bit of an argument, and he brought the talking point "What do you do about third-party dependencies and built-in dependencies?" I told him that there's usually a way to test-double those concerns out (fakes, mocks, stubs), but he wouldn't budge.

What to do about this situation to convince him that this refactoring is indeed necessary, and failing that, what should I do about this situation?

UPDATE: The method under test has other business dependencies, defined thus:

// GetToken returns the token having the specified `tokenType` and `scope`
//
// Parameters:
// - `tokenType`
// - `scope`
//
// Returns:
// - pointer to Token having `tokenType`,`scope` or nil
func (ath *APITokenHandlerSt) GetToken(tokenType oAuth2.OAuth2Type, scope string) *APITokenSt {
    if ath == nil {
        return nil
    }
    if i := ath.FindToken(tokenType, scope); i == -1 {
        return nil
    } else {
        return &ath.Tokens[i]
    }
}

// FindToken finds token with specified `scope` and `tokenType`
//
// Parameters:
// - `tokenType` :  the `OAuth2Type` of the token we want
// - `scope`: the scope of the token we want
//
// Returns:
// - the index of the token in ath.Tokens, or -1 if it cannot be found
func (ath *APITokenHandlerSt) FindToken(tokenType oAuth2.OAuth2Type, scope string) int {
    for i, t := range ath.Tokens {
        if t.TokenType == tokenType &&
            t.Scope == scope {
            return i
        }
    }
    return -1
}

1 Answer 1

1

As a Go developer, I make heavy use of the mockery and testify packages. Using mocks is the industry-standard way for Go developers to achieve a reasonable level of code coverage.

You might try showing your boss some of the problems that are hard to solve without resorting to mocks and without refactoring.

If that doesn't work, you might consider changing bosses or finding a new job. There is plenty of demand for Go developers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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