README.md
1# gock [![Build Status](https://travis-ci.org/h2non/gock.svg?branch=master)](https://travis-ci.org/h2non/gock) [![GitHub release](https://img.shields.io/badge/version-v1.0-orange.svg?style=flat)](https://github.com/h2non/gock/releases) [![GoDoc](https://godoc.org/github.com/h2non/gock?status.svg)](https://godoc.org/github.com/h2non/gock) [![Coverage Status](https://coveralls.io/repos/github/h2non/gock/badge.svg?branch=master)](https://coveralls.io/github/h2non/gock?branch=master) [![Go Report Card](https://img.shields.io/badge/go_report-A+-brightgreen.svg)](https://goreportcard.com/report/github.com/h2non/gock) [![license](https://img.shields.io/badge/license-MIT-blue.svg)]()
2
3Versatile HTTP mocking made easy in [Go](https://golang.org) that works with any `net/http` based stdlib implementation.
4
5Heavily inspired by [nock](https://github.com/node-nock/nock).
6There is also its Python port, [pook](https://github.com/h2non/pook).
7
8To get started, take a look to the [examples](#examples).
9
10## Features
11
12- Simple, expressive, fluent API.
13- Semantic API DSL for declarative HTTP mock declarations.
14- Built-in helpers for easy JSON/XML mocking.
15- Supports persistent and volatile TTL-limited mocks.
16- Full regular expressions capable HTTP request mock matching.
17- Designed for both testing and runtime scenarios.
18- Match request by method, URL params, headers and bodies.
19- Extensible and pluggable HTTP matching rules.
20- Ability to switch between mock and real networking modes.
21- Ability to filter/map HTTP requests for accurate mock matching.
22- Supports map and filters to handle mocks easily.
23- Wide compatible HTTP interceptor using `http.RoundTripper` interface.
24- Works with any `net/http` compatible client, such as [gentleman](https://github.com/h2non/gentleman).
25- Network timeout/cancelation delay simulation.
26- Extensible and hackable API.
27- Dependency free.
28
29## Installation
30
31```bash
32go get -u gopkg.in/h2non/gock.v1
33```
34
35## API
36
37See [godoc reference](https://godoc.org/github.com/h2non/gock) for detailed API documentation.
38
39## How it mocks
40
411. Intercepts any HTTP outgoing request via `http.DefaultTransport` or custom `http.Transport` used by any `http.Client`.
422. Matches outgoing HTTP requests against a pool of defined HTTP mock expectations in FIFO declaration order.
433. If at least one mock matches, it will be used in order to compose the mock HTTP response.
444. If no mock can be matched, it will resolve the request with an error, unless real networking mode is enable, in which case a real HTTP request will be performed.
45
46## Tips
47
48#### Testing
49
50Declare your mocks before you start declaring the concrete test logic:
51
52```go
53func TestFoo(t *testing.T) {
54 defer gock.Off() // Flush pending mocks after test execution
55
56 gock.New("http://server.com").
57 Get("/bar").
58 Reply(200).
59 JSON(map[string]string{"foo": "bar"})
60
61 // Your test code starts here...
62}
63```
64
65#### Race conditions
66
67If you're running concurrent code, be aware that your mocks are declared first to avoid unexpected
68race conditions while configuring `gock` or intercepting custom HTTP clients.
69
70`gock` is not fully thread-safe, but sensible parts are.
71Any help making `gock` more reliable in this sense is appreciated.
72
73#### Define complex mocks first
74
75If you're mocking a bunch of mocks in the same test suite, it's recommended to define the more
76concrete mocks first, and then the generic ones.
77
78This approach usually avoids matching unexpected generic mocks (e.g: specific header, body payload...) instead of the generic ones that performs less complex matches.
79
80#### Disable `gock` traffic interception once done
81
82In other to minimize potential side effects within your test code, it's a good practice
83disabling `gock` once you are done with your HTTP testing logic.
84
85A Go idiomatic approach for doing this can be using it in a `defer` statement, such as:
86
87```go
88func TestGock (t *testing.T) {
89 defer gock.Off()
90
91 // ... my test code goes here
92}
93```
94
95#### Intercept an `http.Client` just once
96
97You don't need to intercept multiple times the same `http.Client` instance.
98
99Just call `gock.InterceptClient(client)` once, typically at the beginning of your test scenarios.
100
101#### Restore an `http.Client` after interception
102
103**NOTE**: this is not required is you are using `http.DefaultClient` or `http.DefaultTransport`.
104
105As a good testing pattern, you should call `gock.RestoreClient(client)` after running your test scenario, typically as after clean up hook.
106
107You can also use a `defer` statement for doing it, as you do with `gock.Off()`, such as:
108
109```go
110func TestGock (t *testing.T) {
111 defer gock.Off()
112 defer gock.RestoreClient(client)
113
114 // ... my test code goes here
115}
116```
117
118## Examples
119
120See [examples](https://github.com/h2non/gock/tree/master/_examples) directory for more featured use cases.
121
122#### Simple mocking via tests
123
124```go
125package test
126
127import (
128 "github.com/nbio/st"
129 "gopkg.in/h2non/gock.v1"
130 "io/ioutil"
131 "net/http"
132 "testing"
133)
134
135func TestSimple(t *testing.T) {
136 defer gock.Off()
137
138 gock.New("http://foo.com").
139 Get("/bar").
140 Reply(200).
141 JSON(map[string]string{"foo": "bar"})
142
143 res, err := http.Get("http://foo.com/bar")
144 st.Expect(t, err, nil)
145 st.Expect(t, res.StatusCode, 200)
146
147 body, _ := ioutil.ReadAll(res.Body)
148 st.Expect(t, string(body)[:13], `{"foo":"bar"}`)
149
150 // Verify that we don't have pending mocks
151 st.Expect(t, gock.IsDone(), true)
152}
153```
154
155#### Request headers matching
156
157```go
158package test
159
160import (
161 "github.com/nbio/st"
162 "gopkg.in/h2non/gock.v1"
163 "io/ioutil"
164 "net/http"
165 "testing"
166)
167
168func TestMatchHeaders(t *testing.T) {
169 defer gock.Off()
170
171 gock.New("http://foo.com").
172 MatchHeader("Authorization", "^foo bar$").
173 MatchHeader("API", "1.[0-9]+").
174 HeaderPresent("Accept").
175 Reply(200).
176 BodyString("foo foo")
177
178 req, err := http.NewRequest("GET", "http://foo.com", nil)
179 req.Header.Set("Authorization", "foo bar")
180 req.Header.Set("API", "1.0")
181 req.Header.Set("Accept", "text/plain")
182
183 res, err := (&http.Client{}).Do(req)
184 st.Expect(t, err, nil)
185 st.Expect(t, res.StatusCode, 200)
186 body, _ := ioutil.ReadAll(res.Body)
187 st.Expect(t, string(body), "foo foo")
188
189 // Verify that we don't have pending mocks
190 st.Expect(t, gock.IsDone(), true)
191}
192```
193
194#### Request param matching
195
196```go
197package test
198
199import (
200 "github.com/nbio/st"
201 "gopkg.in/h2non/gock.v1"
202 "io/ioutil"
203 "net/http"
204 "testing"
205)
206
207func TestMatchParams(t *testing.T) {
208 defer gock.Off()
209
210 gock.New("http://foo.com").
211 MatchParam("page", "1").
212 MatchParam("per_page", "10").
213 Reply(200).
214 BodyString("foo foo")
215
216 req, err := http.NewRequest("GET", "http://foo.com?page=1&per_page=10", nil)
217
218 res, err := (&http.Client{}).Do(req)
219 st.Expect(t, err, nil)
220 st.Expect(t, res.StatusCode, 200)
221 body, _ := ioutil.ReadAll(res.Body)
222 st.Expect(t, string(body), "foo foo")
223
224 // Verify that we don't have pending mocks
225 st.Expect(t, gock.IsDone(), true)
226}
227```
228
229#### JSON body matching and response
230
231```go
232package test
233
234import (
235 "bytes"
236 "github.com/nbio/st"
237 "gopkg.in/h2non/gock.v1"
238 "io/ioutil"
239 "net/http"
240 "testing"
241)
242
243func TestMockSimple(t *testing.T) {
244 defer gock.Off()
245
246 gock.New("http://foo.com").
247 Post("/bar").
248 MatchType("json").
249 JSON(map[string]string{"foo": "bar"}).
250 Reply(201).
251 JSON(map[string]string{"bar": "foo"})
252
253 body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
254 res, err := http.Post("http://foo.com/bar", "application/json", body)
255 st.Expect(t, err, nil)
256 st.Expect(t, res.StatusCode, 201)
257
258 resBody, _ := ioutil.ReadAll(res.Body)
259 st.Expect(t, string(resBody)[:13], `{"bar":"foo"}`)
260
261 // Verify that we don't have pending mocks
262 st.Expect(t, gock.IsDone(), true)
263}
264```
265
266#### Mocking a custom http.Client and http.RoundTripper
267
268```go
269package test
270
271import (
272 "github.com/nbio/st"
273 "gopkg.in/h2non/gock.v1"
274 "io/ioutil"
275 "net/http"
276 "testing"
277)
278
279func TestClient(t *testing.T) {
280 defer gock.Off()
281
282 gock.New("http://foo.com").
283 Reply(200).
284 BodyString("foo foo")
285
286 req, err := http.NewRequest("GET", "http://foo.com", nil)
287 client := &http.Client{Transport: &http.Transport{}}
288 gock.InterceptClient(client)
289
290 res, err := client.Do(req)
291 st.Expect(t, err, nil)
292 st.Expect(t, res.StatusCode, 200)
293 body, _ := ioutil.ReadAll(res.Body)
294 st.Expect(t, string(body), "foo foo")
295
296 // Verify that we don't have pending mocks
297 st.Expect(t, gock.IsDone(), true)
298}
299```
300
301#### Enable real networking
302
303```go
304package main
305
306import (
307 "fmt"
308 "gopkg.in/h2non/gock.v1"
309 "io/ioutil"
310 "net/http"
311)
312
313func main() {
314 defer gock.Off()
315 defer gock.DisableNetworking()
316
317 gock.EnableNetworking()
318 gock.New("http://httpbin.org").
319 Get("/get").
320 Reply(201).
321 SetHeader("Server", "gock")
322
323 res, err := http.Get("http://httpbin.org/get")
324 if err != nil {
325 fmt.Errorf("Error: %s", err)
326 }
327
328 // The response status comes from the mock
329 fmt.Printf("Status: %d\n", res.StatusCode)
330 // The server header comes from mock as well
331 fmt.Printf("Server header: %s\n", res.Header.Get("Server"))
332 // Response body is the original
333 body, _ := ioutil.ReadAll(res.Body)
334 fmt.Printf("Body: %s", string(body))
335}
336```
337
338#### Debug intercepted http requests
339
340```go
341package main
342
343import (
344 "bytes"
345 "gopkg.in/h2non/gock.v1"
346 "net/http"
347)
348
349func main() {
350 defer gock.Off()
351 gock.Observe(gock.DumpRequest)
352
353 gock.New("http://foo.com").
354 Post("/bar").
355 MatchType("json").
356 JSON(map[string]string{"foo": "bar"}).
357 Reply(200)
358
359 body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
360 http.Post("http://foo.com/bar", "application/json", body)
361}
362
363```
364
365## Hacking it!
366
367You can easily hack `gock` defining custom matcher functions with own matching rules.
368
369See [add matcher functions](https://github.com/h2non/gock/blob/master/_examples/add_matchers/matchers.go) and [custom matching layer](https://github.com/h2non/gock/blob/master/_examples/custom_matcher/matcher.go) examples for further details.
370
371## License
372
373MIT - Tomas Aparicio
374