Mock tests and 0% coverage?

I have a small piece of code that goes communicates with GitHub API. It authenticates myself using my Token. It reads the token from my shell environment

export GHTOKEN=“foobar”

And then, after getting authenticated, it visits one my github repositories (specifically the one called “ghostfish”) and it fetches the description of it.

So I wanted to write some tests for it. That’s when all the confusion started … I didn’t want to spam github with my test, so I wanted to mock the function in a way so to avoid hitting the actual 3rd party service – that is github api.

What I did:

  • I converted my functions into methods
  • I created an interface that implements those methods
  • I used mockgen to generate the mock. Specifically the command was: mockgen -source=main.go -destination=mocks/mock_githubClient.go -package=$GOPACKAGE

Then, when I test for the coverage, I get 0%.

$ go tool cover -func cover.out                                                      
github.com/testf/main.go:24:    main            0.0%
github.com/testf/main.go:34:    Authenticated   0.0%
github.com/testf/main.go:44:    GetUserRepo     0.0%
total:                          (statements)    0.0%

How is that possible? I am confused …

Here’s the code, if you want to have a look:

main.go

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/google/go-github/v30/github"
	"golang.org/x/oauth2"
)

var (
	GithubClient githubClientInterface = &githubClient{}
)

type githubClientInterface interface {
	Authenticated(env string) *github.Client
	GetUserRepo(client *github.Client, user string) (*github.Repository, error)
}

type githubClient struct{}

func main() {
	client := GithubClient.Authenticated("GHTOKEN")
	repo, err := GithubClient.GetUserRepo(client, "drpaneas")
	if err != nil {
		fmt.Println("Error")
		log.Fatal(err)
	}
	fmt.Println(repo.GetDescription())
}

func (c *githubClient) Authenticated(env string) *github.Client {
	token := oauth2.Token{
		AccessToken: os.Getenv(env),
	}
	tokenSource := oauth2.StaticTokenSource(&token)
	tc := oauth2.NewClient(context.Background(), tokenSource)
	client := github.NewClient(tc)
	return client
}

func (c *githubClient) GetUserRepo(client *github.Client, user string) (*github.Repository, error) {
	repo, _, err := client.Repositories.Get(context.Background(), user, "ghostfish")
	if err != nil {
		return nil, err
	}
	return repo, err
}

main_test.go

package main

import (
	"fmt"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/google/go-github/v30/github"
	mocks "github.com/testf/mocks"
)

func TestWithGoMock(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockgithubClient := mocks.NewMockgithubClientInterface(mockCtrl)
	c := &github.Client{}
	mockgithubClient.EXPECT().Authenticated("whatever").AnyTimes().Return(c)
	client := mockgithubClient.Authenticated("whatever")

	s := "something"
	var x *string
	x = &s
	r := &github.Repository{Description: x}
	mockgithubClient.EXPECT().GetUserRepo(client, "drpaneas").AnyTimes().Return(r, nil)
	repo, _ := mockgithubClient.GetUserRepo(client, "drpaneas")
	output := fmt.Sprintf("%s", (repo.GetDescription()))
	if output != "something" {
		t.Error("It cannot fetch the repository name")
	}
}

mocks/mock_githubClient.go

// Code generated by MockGen. DO NOT EDIT.
// Source: main.go

// Package mock_main is a generated GoMock package.
package mock_main

import (
	gomock "github.com/golang/mock/gomock"
	github "github.com/google/go-github/v30/github"
	reflect "reflect"
)

// MockgithubClientInterface is a mock of githubClientInterface interface
type MockgithubClientInterface struct {
	ctrl     *gomock.Controller
	recorder *MockgithubClientInterfaceMockRecorder
}

// MockgithubClientInterfaceMockRecorder is the mock recorder for MockgithubClientInterface
type MockgithubClientInterfaceMockRecorder struct {
	mock *MockgithubClientInterface
}

// NewMockgithubClientInterface creates a new mock instance
func NewMockgithubClientInterface(ctrl *gomock.Controller) *MockgithubClientInterface {
	mock := &MockgithubClientInterface{ctrl: ctrl}
	mock.recorder = &MockgithubClientInterfaceMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockgithubClientInterface) EXPECT() *MockgithubClientInterfaceMockRecorder {
	return m.recorder
}

// Authenticated mocks base method
func (m *MockgithubClientInterface) Authenticated(env string) *github.Client {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Authenticated", env)
	ret0, _ := ret[0].(*github.Client)
	return ret0
}

// Authenticated indicates an expected call of Authenticated
func (mr *MockgithubClientInterfaceMockRecorder) Authenticated(env interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authenticated", reflect.TypeOf((*MockgithubClientInterface)(nil).Authenticated), env)
}

// GetUserRepo mocks base method
func (m *MockgithubClientInterface) GetUserRepo(client *github.Client, user string) (*github.Repository, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "GetUserRepo", client, user)
	ret0, _ := ret[0].(*github.Repository)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// GetUserRepo indicates an expected call of GetUserRepo
func (mr *MockgithubClientInterfaceMockRecorder) GetUserRepo(client, user interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserRepo", reflect.TypeOf((*MockgithubClientInterface)(nil).GetUserRepo), client, user)
}