Skip to content

A library to aid unittesting code that uses Golang's Github SDK

License

Notifications You must be signed in to change notification settings

antonlisovenko/go-github-mock

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-github-mock

Go Reference Go Report Card

A library to aid unittesting code that uses Golang's Github SDK

Installation

go get github.com/migueleliasweb/go-github-mock

Features

  • Create mocks for successive calls for the same endpoint
  • Mock error returns
  • High level abstraction helps writing readabe unittests (see mock.WithRequestMatch)
  • Lower level abstraction for advanced uses (see mock.WithRequestMatchHandler)

Example

import "github.com/migueleliasweb/go-github-mock/src/mock"

Multiple requests

mockedHTTPClient := mock.NewMockedHTTPClient(
    mock.WithRequestMatch(
        mock.GetUsersByUsername,
        [][]byte{
            mock.MustMarshal(github.User{
                Name: github.String("foobar"),
            }),
        },
    ),
    mock.WithRequestMatch(
        mock.GetUsersOrgsByUsername,
        [][]byte{
            mock.MustMarshal([]github.Organization{
                {
                    Name: github.String("foobar123thisorgwasmocked"),
                },
            }),
        },
    ),
    mock.WithRequestMatchHandler(
        mock.GetOrgsProjectsByOrg,
        http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
            w.Write(mock.MustMarshal([]github.Project{
                {
                    Name: github.String("mocked-proj-1"),
                },
                {
                    Name: github.String("mocked-proj-2"),
                },
            }))
        }),
    ),
)
c := github.NewClient(mockedHTTPClient)

ctx := context.Background()

user, _, userErr := c.Users.Get(ctx, "myuser")

// user.Name == "foobar"

orgs, _, orgsErr := c.Organizations.List(
    ctx,
    *(user.Name),
    nil,
)

// orgs[0].Name == "foobar123thisorgwasmocked"

projs, _, projsErr := c.Organizations.ListProjects(
    ctx,
    *orgs[0].Name,
    &github.ProjectListOptions{},
)

// projs[0].Name == "mocked-proj-1"
// projs[1].Name == "mocked-proj-2"

Mocking errors from the API

mockedHTTPClient := mock.NewMockedHTTPClient(
    mock.WithRequestMatchHandler(
        mock.GetUsersByUsername,
        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            WriteError(
                w,
                http.StatusInternalServerError,
                "github went belly up or something",
            )
        }),
    ),
)
c := github.NewClient(mockedHTTPClient)

ctx := context.Background()

user, _, userErr := c.Users.Get(ctx, "someUser")

// user == nil

if userErr == nil {	
    if ghErr, ok := userErr.(*github.ErrorResponse); ok {
        fmt.Println(ghErr.Message) // == "github went belly up or something"
    }
}

Mocking with pagination

mockedHTTPClient := NewMockedHTTPClient(
    WithRequestMatchPages(
        GetOrgsReposByOrg,
        [][]byte{
            MustMarshal([]github.Repository{
                {
                    Name: github.String("repo-A-on-first-page"),
                },
                {
                    Name: github.String("repo-B-on-first-page"),
                },
            }),
            MustMarshal([]github.Repository{
                {
                    Name: github.String("repo-C-on-second-page"),
                },
                {
                    Name: github.String("repo-D-on-second-page"),
                },
            }),
        },
    ),
)

c := github.NewClient(mockedHTTPClient)

ctx := context.Background()

opt := &github.RepositoryListByOrgOptions{
    ListOptions: github.ListOptions{
        // in fact, the perPage option is ignored my the mocks
        // but this would be present in production code
        PerPage: 2,
    },
}

var allRepos []*github.Repository

for {
    repos, resp, listErr := c.Repositories.ListByOrg(ctx, "foobar", opt)

    if listErr != nil {
        t.Errorf("error listing repositories: %s", listErr.Error())
    }

    // len(repos) == 2

    allRepos = append(allRepos, repos...)

    if resp.NextPage == 0 {
        break
    }

    opt.Page = resp.NextPage
}

// matches the mock definitions (len(page[0]) + len(page[1])
// len(allRepos) == 4

Why

Some conversations got started on go-github#1800 since go-github didn't provide an interface that could be easily reimplemented for unittests. After lots of conversations from the folks from go-github and quite a few PR ideas later, this style of testing was deemed not suitable to be part of the core SDK as it's not a feature of the API itself. Nonetheless, the ability of writing unittests for code that uses the go-github package is critical.

A reuseable, and not overly verbose, way of writing the tests was reached after some more interactions (months down the line) and here we are.

Thanks

Thanks for all ideas and feedback from the folks in go-github.

License

This library is distributed under the MIT License found in LICENSE.

About

A library to aid unittesting code that uses Golang's Github SDK

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 100.0%