Git Product home page Git Product logo

go-github-mock's Introduction

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
  • Pagination support
  • Mock error returns
  • High level abstraction helps writing readabe unittests (see mock.WithRequestMatch)
  • Lower level abstraction for advanced uses (see mock.WithRequestMatchHandler)

Breaking changes

  • v0.0.3 the API for the server options have beem simplified
  • v0.0.4 fixes to the gen script caused multiple url matches to change

Example

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

Multiple requests

mockedHTTPClient := mock.NewMockedHTTPClient(
    mock.WithRequestMatch(
        mock.GetUsersByUsername,
        github.User{
            Name: github.String("foobar"),
        },
    ),
    mock.WithRequestMatch(
        mock.GetUsersOrgsByUsername,
        []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"

Returning empty results

mockedHTTPClient := NewMockedHTTPClient(
    WithRequestMatch(
        GetReposIssuesByOwnerByRepo,
        []github.Issue{
            {
                ID:    github.Int64(123),
                Title: github.String("Issue 1"),
            },
            {
                ID:    github.Int64(456),
                Title: github.String("Issue 2"),
            },
        },
        []github.Issue{},
    ),
)

c := github.NewClient(mockedHTTPClient)

ctx := context.Background()

issues1, _, repo1Err := c.Issues.ListByRepo(ctx, "owner1", "repo1", &github.IssueListByRepoOptions{})

// len(issues1) == 2
// repo1Err == nil

issues2, _, repo2Err := c.Issues.ListByRepo(ctx, "owner1", "repo2", &github.IssueListByRepoOptions{})

// len(issues2) == 0
// repo2Err == nil

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,
        []github.Repository{
            {
                Name: github.String("repo-A-on-first-page"),
            },
            {
                Name: github.String("repo-B-on-first-page"),
            },
        },
        []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.

go-github-mock's People

Contributors

migueleliasweb avatar mheck136 avatar mp3000mp avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.