1# 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) 2 3Easy mocking of http responses from external resources. 4 5## Install 6 7Currently supports Go 1.7 - 1.12. 8 9`v1` branch has to be used instead of `master`. 10 11 12### Using go modules (aka. `go mod`) 13 14In your go files, simply use: 15```go 16import "github.com/jarcoal/httpmock" 17``` 18 19Then next `go mod tidy` or `go test` invocation will automatically 20populate your `go.mod` with the last httpmock release, now 21[![Version](https://img.shields.io/github/tag/jarcoal/httpmock.svg)](https://github.com/jarcoal/httpmock/releases). 22 23Note you can use `go mod vendor` to vendor your dependencies. 24 25 26### Using `$GOPATH` 27 28`v1` branch is configured as the default branch in github, so: 29``` 30go get github.com/jarcoal/httpmock 31``` 32 33automatically downloads the `v1` branch in `$GOPATH/src`. Then in your 34go files use: 35```go 36import "github.com/jarcoal/httpmock" 37``` 38 39 40### Vendoring, using [`govendor`](https://github.com/kardianos/govendor) for example 41 42When vendoring is used, `v1` branch has to be specified. Two choices here: 43 44- preferred way: 45 ``` 46 govendor fetch github.com/jarcoal/httpmock@v1 47 ``` 48 then in go files: 49 ```go 50 import "github.com/jarcoal/httpmock" 51 ``` 52- old way (before `v1` was set as default branch), use gopkg to read from 53 `v1` branch: 54 ``` 55 govendor fetch gopkg.in/jarcoal/httpmock.v1 56 ``` 57 then in go files: 58 ```go 59 import "gopkg.in/jarcoal/httpmock.v1" 60 ``` 61 62 63## Usage 64 65### Simple Example: 66```go 67func TestFetchArticles(t *testing.T) { 68 httpmock.Activate() 69 defer httpmock.DeactivateAndReset() 70 71 // Exact URL match 72 httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles", 73 httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`)) 74 75 // Regexp match (could use httpmock.RegisterRegexpResponder instead) 76 httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/\d+\z`, 77 httpmock.NewStringResponder(200, `{"id": 1, "name": "My Great Article"}`)) 78 79 // do stuff that makes a request to articles 80 ... 81 82 // get count info 83 httpmock.GetTotalCallCount() 84 85 // get the amount of calls for the registered responder 86 info := httpmock.GetCallCountInfo() 87 info["GET https://api.mybiz.com/articles"] // number of GET calls made to https://api.mybiz.com/articles 88 info["GET https://api.mybiz.com/articles/id/12"] // number of GET calls made to https://api.mybiz.com/articles/id/12 89 info[`GET =~^https://api\.mybiz\.com/articles/id/\d+\z`] // number of GET calls made to https://api.mybiz.com/articles/id/<any-number> 90} 91``` 92 93### Advanced Example: 94```go 95func TestFetchArticles(t *testing.T) { 96 httpmock.Activate() 97 defer httpmock.DeactivateAndReset() 98 99 // our database of articles 100 articles := make([]map[string]interface{}, 0) 101 102 // mock to list out the articles 103 httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles", 104 func(req *http.Request) (*http.Response, error) { 105 resp, err := httpmock.NewJsonResponse(200, articles) 106 if err != nil { 107 return httpmock.NewStringResponse(500, ""), nil 108 } 109 return resp, nil 110 }, 111 ) 112 113 // return an article related to the request with the help of regexp submatch (\d+) 114 httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/(\d+)\z`, 115 func(req *http.Request) (*http.Response, error) { 116 // Get ID from request 117 id := httpmock.MustGetSubmatchAsUint(req, 1) // 1=first regexp submatch 118 return httpmock.NewJsonResponse(200, map[string]interface{}{ 119 "id": id, 120 "name": "My Great Article", 121 }) 122 }, 123 ) 124 125 // mock to add a new article 126 httpmock.RegisterResponder("POST", "https://api.mybiz.com/articles", 127 func(req *http.Request) (*http.Response, error) { 128 article := make(map[string]interface{}) 129 if err := json.NewDecoder(req.Body).Decode(&article); err != nil { 130 return httpmock.NewStringResponse(400, ""), nil 131 } 132 133 articles = append(articles, article) 134 135 resp, err := httpmock.NewJsonResponse(200, article) 136 if err != nil { 137 return httpmock.NewStringResponse(500, ""), nil 138 } 139 return resp, nil 140 }, 141 ) 142 143 // do stuff that adds and checks articles 144} 145``` 146 147### Algorithm 148 149When `GET http://example.tld/some/path?b=12&a=foo&a=bar` request is 150caught, all standard responders are checked against the following URL 151or paths, the first match stops the search: 152 1531. `http://example.tld/some/path?b=12&a=foo&a=bar` (original URL) 1541. `http://example.tld/some/path?a=bar&a=foo&b=12` (sorted query params) 1551. `http://example.tld/some/path` (without query params) 1561. `/some/path?b=12&a=foo&a=bar` (original URL without scheme and host) 1571. `/some/path?a=bar&a=foo&b=12` (same, but sorted query params) 1581. `/some/path` (path only) 159 160If no standard responder matched, the regexp responders are checked, 161in the same order, the first match stops the search. 162 163 164### [Ginkgo](https://onsi.github.io/ginkgo/) Example: 165```go 166// article_suite_test.go 167 168import ( 169 // ... 170 "github.com/jarcoal/httpmock" 171) 172// ... 173var _ = BeforeSuite(func() { 174 // block all HTTP requests 175 httpmock.Activate() 176}) 177 178var _ = BeforeEach(func() { 179 // remove any mocks 180 httpmock.Reset() 181}) 182 183var _ = AfterSuite(func() { 184 httpmock.DeactivateAndReset() 185}) 186 187 188// article_test.go 189 190import ( 191 // ... 192 "github.com/jarcoal/httpmock" 193) 194 195var _ = Describe("Articles", func() { 196 It("returns a list of articles", func() { 197 httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles.json", 198 httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`)) 199 200 // do stuff that makes a request to articles.json 201 }) 202}) 203``` 204 205### [Ginkgo](https://onsi.github.io/ginkgo/) + [Resty](https://github.com/go-resty/resty) Example: 206```go 207// article_suite_test.go 208 209import ( 210 // ... 211 "github.com/jarcoal/httpmock" 212 "github.com/go-resty/resty" 213) 214// ... 215var _ = BeforeSuite(func() { 216 // block all HTTP requests 217 httpmock.ActivateNonDefault(resty.DefaultClient.GetClient()) 218}) 219 220var _ = BeforeEach(func() { 221 // remove any mocks 222 httpmock.Reset() 223}) 224 225var _ = AfterSuite(func() { 226 httpmock.DeactivateAndReset() 227}) 228 229 230// article_test.go 231 232import ( 233 // ... 234 "github.com/jarcoal/httpmock" 235 "github.com/go-resty/resty" 236) 237 238var _ = Describe("Articles", func() { 239 It("returns a list of articles", func() { 240 fixture := `{"status":{"message": "Your message", "code": 200}}` 241 responder := httpmock.NewStringResponder(200, fixture) 242 fakeUrl := "https://api.mybiz.com/articles.json" 243 httpmock.RegisterResponder("GET", fakeUrl, responder) 244 245 // fetch the article into struct 246 articleObject := &models.Article{} 247 _, err := resty.R().SetResult(articleObject).Get(fakeUrl) 248 249 // do stuff with the article object ... 250 }) 251}) 252``` 253