Original post

I have a library package that calls out to an web service, to authenticate credentials. It’s basically this:

package myauthsvc

// Client object
type Client struct {
        AccessToken string
        Secret      string
}

func (c *Client) Authenticate(userID string, secret string) (bool, error) {
    // do some http call, return the result
    return false, nil
}

It’s working fine, but obviously hard to mock and not idiomatic. I’m reasonably clear about interfaces, when used between packages. But I’m getting confused about struct embedding specifically when it comes to a library package.

If I want to improve my implementation, is this correct?

package myauthsvc

type ClientInterface interface {
        Authenticate(userID string, secret string) (bool, error)
}

type Client struct {
        AccessToken string
        Secret      string
        ClientInterface
}

func (c *Client) Authenticate(userID string, secret string) (bool, error) {
        // do some stuff

        return false, nil
}

func NewClient(token string, secret string, ci ClientInterface) *Client {
        return &Client{token, secret, ci}
}

Firstly, The NewClient method is defined in the package, but outside the type/interface.

Secondly, the Authenticate method has a receiving type of Client. And I think this is where my lack of understanding is. Is there something implicit that means Client ‘conforms’ to ClientInterface, and selector shorthand is causing Client.Authenticate == Client.ClientInterface.Authenticate?

Lastly, if my library package is self-contained but I want to be able to make it easy for other developers to mock the methods, how does the implementation look?

package main

import (
    "github.com/thisdougb/myauthsvc" // this repo is just as an example, doesn't really exist
)
        
func test() {
        c := myauthsvc.NewClient("userid", "secret", ci) // <- where does ci come from?
}

How do I create ‘ci’ when creating a new client?

I have read through solid-go-design
And also type-embedding

thanks for help.

For anyone else on the same path, I think I’ve figured out where I was going wrong after trying out a good few things.

My motivation is to make my library package easy to mock, for other people. This isn’t what ‘embedding interfaces in structs’ does, it turns out. That’s my first deduction.

To make a library package easy to mock/test for the people using it, you include an interface type. This ClientInterface (package interface?) can then be used in method signatures as a parameter type. This, specifically, is what I was getting wrong.

package myauthsvc

type ClientInterface interface {
        Authenticate(userID string, secret string) bool
}

type Client struct {
        AccessToken string
        Secret      string
}

func (c Client) Authenticate(userID string, secret string) (bool, error) {
        // do some stuff with c.AccessToken and c.Secret
        return false
}

So, when someone wants to use myauthsvc in their own code we use normal dependency injection. This is possible because myauthsvc implements an interface. In standard ‘interface’ usage, ci accepts anything that implements the methods of ClientInterface.

// handler.
import (
    "github.com/thisdougb/myauthsvc" // this repo is just as an example, doesn't really exist
)

func auth(userID string, secret string, ci myauthsvc.ClientInterface) bool {
        return ci.Authenticate(userID, secret)
}

What this then allows us to do, is to mock myauthsvc for our tests. I setup a test like this. A mock struct that also conforms to ClientInterface.

// handler_test.go
type MockAuthSvc struct{}

func (m MockAuthSvc) Authenticate(userID string, secret string) bool {
        return true
}

func TestAuth(t *testing.T) {

    var mockC MockAuthSvc
        assert.Equal(t, true, auth("userID", "secret", mockC)
}

So I can import myauthsvc as a third party library, and mock it easily to be able to run my own package tests.

I hope you can find those articles related to your case very useful:

https://github.com/golang/go/wiki/CodeReviewComments#interfaces WIKI

https://www.ardanlabs.com/blog/2016/10/avoid-interface-pollution.html

https://rakyll.org/interface-pollution/ – comparison with C++/Java from Joanna

https://blog.chewxy.com/2018/03/18/golang-interfaces/ — Java comparison

Golang and interfaces misuse

Francesc Capmpy’s presentation “understanding the interface”: