The recorder is a small helper package, primarily intended to help with unit tests. It is capable of recording and replaying traffic, avoiding real network requests.
In the default mode, responses are read from disk, allowing the network roundtrip to be avoided. This can be useful for a couple reasons:
- Faster
- Works offline
- Reduces side-effects on called APIs (such as when testing cloud service provider endpoints)
In addition, with the Passthrough
mode, requests can be recorded an asserted
in unit tests.
Unless the mode is set to Passthrough
, the request-response is recorded in a
yml
on disk, such as:
# request 0
# timestamp 2019-04-30 10:02:04 +0000 UTC
# roundtrip 398ms
request:
method: POST
url: https://jsonplaceholder.typicode.com/posts
headers:
Content-Type: application/json
response:
status_code: 201
headers:
Cache-Control: no-cache
Content-Length: '69'
Content-Type: application/json; charset=utf-8
Date: Tue, 30 Apr 2019 10:02:04 GMT
Location: http://jsonplaceholder.typicode.com/posts/101
Pragma: no-cache
body: |-
{
"title": "hello",
"body": "world",
"userId": 1,
"id": 101
}
// Create a new recorder.
// Data will be saved in testdata/example.yml
rec := recorder.New("testdata/example")
// Create HTTP client with recorder transport
cli := &http.Client{
Transport: rec,
}
// Perform a request
resp, err := cli.Get("https://jsonplaceholder.typicode.com/posts/1")
if err != nil {
log.Fatal(err)
}
// Response is only done if required
b, err := httputil.DumpResponse(resp, true)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
Modes allow granular control of behavior.
Mode | Behavior |
---|---|
Auto |
Perform network requests if no stored file exists |
ReplayOnly |
Do not allow network traffic, only return stored files |
Record |
Always perform request and overwrite existing files |
Passthrough |
No files are saved on disk but requests can be retrieved with Lookup() |
If no mode is set, Auto
is used.
The Passthrough
mode disabled loading and saving files but can be useful for
asserting if the expected requests were made in tests.
Filters allow removing sensitive data from the saved files.
The filters are executed after the request but before saving files to disk.
This will remove the Authorization
header from the request:
rec := recorder.New("testdata/private-api", recorder.RemoveRequestHeader("Authorization"))
cli := &http.Client{
Transport: rec,
}
req, _ := http.NewRequest("https://example.com", "application/json", strings.NewReader("{}"))
req.Header.Add("Authorization", "abcdef")
_, err := cli.Do(req)
if err != nil {
log.Fatal(err)
}
// Authorization header is not saved to disk
This will remove the Set-Cookie
header from the response:
rec := recorder.New("testdata/private-api", recorder.RemoveResponseHeader("Set-Cookie"))
cli := &http.Client{
Transport: rec,
}
_, err := cli.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
// The saved file will not contain the Set-Cookie header that was set by the server.
In addition to the built in filters, custom filters can be implemented by
passing functions with a signature func (entry *recorder.Entry) {}
.
rec := recorder.New("testdata/request-header", func(e *recorder.Entry) {
// Modify e.Request and e.Response
})
cli := &http.Client{
Transport: rec,
}
_, err := cli.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
This library is inspired by:
- https://github.com/dnaeon/go-vcr
- https://github.com/ad2games/vcr-go
- https://github.com/nock/nock#recording
The reason for writing this library is primarily flexiblity in setting the mode and different API (no VCR references).
MIT