# httpmock [![Build Status](https://travis-ci.org/jarcoal/httpmock.png?branch=v1)](https://travis-ci.org/jarcoal/httpmock) [![Coverage Status](https://coveralls.io/repos/github/jarcoal/httpmock/badge.svg?branch=v1)](https://coveralls.io/github/jarcoal/httpmock?branch=v1) [![GoDoc](https://godoc.org/github.com/jarcoal/httpmock?status.svg)](https://godoc.org/github.com/jarcoal/httpmock) [![Version](https://img.shields.io/github/tag/jarcoal/httpmock.svg)](https://github.com/jarcoal/httpmock/releases) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go/#testing) Easy mocking of http responses from external resources. ## Install Currently supports Go 1.7 - 1.12. `v1` branch has to be used instead of `master`. ### Using go modules (aka. `go mod`) In your go files, simply use: ```go import "github.com/jarcoal/httpmock" ``` Then next `go mod tidy` or `go test` invocation will automatically populate your `go.mod` with the last httpmock release, now [![Version](https://img.shields.io/github/tag/jarcoal/httpmock.svg)](https://github.com/jarcoal/httpmock/releases). Note you can use `go mod vendor` to vendor your dependencies. ### Using `$GOPATH` `v1` branch is configured as the default branch in github, so: ``` go get github.com/jarcoal/httpmock ``` automatically downloads the `v1` branch in `$GOPATH/src`. Then in your go files use: ```go import "github.com/jarcoal/httpmock" ``` ### Vendoring, using [`govendor`](https://github.com/kardianos/govendor) for example When vendoring is used, `v1` branch has to be specified. Two choices here: - preferred way: ``` govendor fetch github.com/jarcoal/httpmock@v1 ``` then in go files: ```go import "github.com/jarcoal/httpmock" ``` - old way (before `v1` was set as default branch), use gopkg to read from `v1` branch: ``` govendor fetch gopkg.in/jarcoal/httpmock.v1 ``` then in go files: ```go import "gopkg.in/jarcoal/httpmock.v1" ``` ## Usage ### Simple Example: ```go func TestFetchArticles(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() // Exact URL match httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles", httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`)) // Regexp match (could use httpmock.RegisterRegexpResponder instead) httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/\d+\z`, httpmock.NewStringResponder(200, `{"id": 1, "name": "My Great Article"}`)) // do stuff that makes a request to articles ... // get count info httpmock.GetTotalCallCount() // get the amount of calls for the registered responder info := httpmock.GetCallCountInfo() info["GET https://api.mybiz.com/articles"] // number of GET calls made to https://api.mybiz.com/articles info["GET https://api.mybiz.com/articles/id/12"] // number of GET calls made to https://api.mybiz.com/articles/id/12 info[`GET =~^https://api\.mybiz\.com/articles/id/\d+\z`] // number of GET calls made to https://api.mybiz.com/articles/id/ } ``` ### Advanced Example: ```go func TestFetchArticles(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() // our database of articles articles := make([]map[string]interface{}, 0) // mock to list out the articles httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles", func(req *http.Request) (*http.Response, error) { resp, err := httpmock.NewJsonResponse(200, articles) if err != nil { return httpmock.NewStringResponse(500, ""), nil } return resp, nil }, ) // return an article related to the request with the help of regexp submatch (\d+) httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/(\d+)\z`, func(req *http.Request) (*http.Response, error) { // Get ID from request id := httpmock.MustGetSubmatchAsUint(req, 1) // 1=first regexp submatch return httpmock.NewJsonResponse(200, map[string]interface{}{ "id": id, "name": "My Great Article", }) }, ) // mock to add a new article httpmock.RegisterResponder("POST", "https://api.mybiz.com/articles", func(req *http.Request) (*http.Response, error) { article := make(map[string]interface{}) if err := json.NewDecoder(req.Body).Decode(&article); err != nil { return httpmock.NewStringResponse(400, ""), nil } articles = append(articles, article) resp, err := httpmock.NewJsonResponse(200, article) if err != nil { return httpmock.NewStringResponse(500, ""), nil } return resp, nil }, ) // do stuff that adds and checks articles } ``` ### Algorithm When `GET http://example.tld/some/path?b=12&a=foo&a=bar` request is caught, all standard responders are checked against the following URL or paths, the first match stops the search: 1. `http://example.tld/some/path?b=12&a=foo&a=bar` (original URL) 1. `http://example.tld/some/path?a=bar&a=foo&b=12` (sorted query params) 1. `http://example.tld/some/path` (without query params) 1. `/some/path?b=12&a=foo&a=bar` (original URL without scheme and host) 1. `/some/path?a=bar&a=foo&b=12` (same, but sorted query params) 1. `/some/path` (path only) If no standard responder matched, the regexp responders are checked, in the same order, the first match stops the search. ### [Ginkgo](https://onsi.github.io/ginkgo/) Example: ```go // article_suite_test.go import ( // ... "github.com/jarcoal/httpmock" ) // ... var _ = BeforeSuite(func() { // block all HTTP requests httpmock.Activate() }) var _ = BeforeEach(func() { // remove any mocks httpmock.Reset() }) var _ = AfterSuite(func() { httpmock.DeactivateAndReset() }) // article_test.go import ( // ... "github.com/jarcoal/httpmock" ) var _ = Describe("Articles", func() { It("returns a list of articles", func() { httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles.json", httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`)) // do stuff that makes a request to articles.json }) }) ``` ### [Ginkgo](https://onsi.github.io/ginkgo/) + [Resty](https://github.com/go-resty/resty) Example: ```go // article_suite_test.go import ( // ... "github.com/jarcoal/httpmock" "github.com/go-resty/resty" ) // ... var _ = BeforeSuite(func() { // block all HTTP requests httpmock.ActivateNonDefault(resty.DefaultClient.GetClient()) }) var _ = BeforeEach(func() { // remove any mocks httpmock.Reset() }) var _ = AfterSuite(func() { httpmock.DeactivateAndReset() }) // article_test.go import ( // ... "github.com/jarcoal/httpmock" "github.com/go-resty/resty" ) var _ = Describe("Articles", func() { It("returns a list of articles", func() { fixture := `{"status":{"message": "Your message", "code": 200}}` responder := httpmock.NewStringResponder(200, fixture) fakeUrl := "https://api.mybiz.com/articles.json" httpmock.RegisterResponder("GET", fakeUrl, responder) // fetch the article into struct articleObject := &models.Article{} _, err := resty.R().SetResult(articleObject).Get(fakeUrl) // do stuff with the article object ... }) }) ```