Skip to content

Mockable, interface-based wrapper for the github.com/google/go-github *Client

License

Notifications You must be signed in to change notification settings

connormckelvey/go-github-mockable

Repository files navigation

Go Github Mockable

Go Reference Go Report Card

Go Github Mockable provides an interface-based wrapper for the Go Github client, making it possible to generate mocks (also included) for the client using GoMock.

Installation

$ go get github.com/connormckelvey/go-github-mockable

Features

Interface type to be used in place of *github.Client. Allows dependency injection of a mocked ClientAPI.

For example

type MyService struct {
    github *github.Client
}

func NewMyService(client *github.Client) *Service {
    return &MyService{
        github: client,
    }
}

//becomes:

type MyService struct {
    github gogithubmockable.ClientAPI
}

func NewMyService(client gogithubmockable.ClientAPI) *Service {
    return &MyService{
        github: client,
    }
}

gogithubmockable.Client provides a default implementation of gogithubmockable.ClientAPI by dispatching calls to a provided *github.Client

For example:

func main() {
    gh := github.NewClient(nil)
    client := gogithubmockable.NewClient(gh)

    service := NewMyService(client)
    ...
}


func TestMyService(t *testing.T) {
    ctrl := gomock.NewController(t)

	mockClient := mocks.NewMockClientAPI(ctrl)
    service := NewMYService(mockClient)
}

The ClientAPI interface include getter methods for every service normally available on the *github.Client struct. The return types for the getter methods are themselves interfaces allowing services to be independently mocked.

For example

func (s *MyService) Foo() {
    repo, _, err := s.github.Repositories.Get(context.Background(), "owner", "repo")
    ...
}

// becomes

func (s *MyService) Foo() {
    repo, _, err := s.github.Repositories().Get(context.Background(), "owner", "repo")
    ...
}

The service getter methods included on the ClientAPI interface return interface types defined with all public methods included on the original concrete service types.

Usage

package main

import (
    "github.com/google/go-github/v48/github"
    "github.com/connormckelvey/go-github-mockable"
)

func main() {
    gh := github.NewClient(nil)
    client := gogithubmockable.NewClient(gh)


    // Instead of client.Repositories, use client.Repositories()
    c.Repositories().Get(context.TODO(), "owner", "repo")
    ...
}

Mocking

func TestMockClientAPI(t *testing.T) {
	ctrl := gomock.NewController(t)
	owner := "connormckelvey"
	repo := "go-github-mockable"
	expected := fmt.Sprintf("%s/%s", owner, repo)

	rm := mocks.NewMockRepositoriesService(ctrl)
	rm.EXPECT().Get(gomock.Any(), gomock.Eq(owner), gomock.Eq(repo)).Return(
		&github.Repository{
			FullName: &expected,
		},
		&github.Response{},
		nil,
	)

	cm := mocks.NewMockClientAPI(ctrl)
	cm.EXPECT().Repositories().Return(rm)

	service := NewMyService(cm)
	fullName, err := service.FullName()
	require.NoError(t, err)
	assert.Equal(t, expected, fullName)
}