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)
}