README.md
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