go-vcr
Record and replay your HTTP interactions for fast, deterministic and accurate tests
Top Related Projects
Quick Overview
go-vcr is a Go library that provides HTTP interaction recording and playback functionality, similar to the VCR gem in Ruby. It allows developers to record HTTP interactions during tests and play them back in subsequent test runs, improving test reliability and speed.
Pros
- Speeds up test execution by eliminating network calls in subsequent runs
- Improves test reliability by removing dependencies on external services
- Supports custom matchers for flexible request matching
- Compatible with Go's standard
http.Client
and can be easily integrated into existing codebases
Cons
- Recorded cassettes may become outdated if the external API changes
- May not accurately represent real-world scenarios if used excessively
- Requires careful management of cassettes to avoid committing sensitive information
- Limited support for streaming responses
Code Examples
Recording a new cassette:
r, err := recorder.New("fixtures/example")
if err != nil {
log.Fatal(err)
}
defer r.Stop()
client := &http.Client{
Transport: r,
}
resp, err := client.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
Using a custom matcher:
r, _ := recorder.New("fixtures/custom_matcher")
r.SetMatcher(func(r *http.Request, i cassette.Request) bool {
return r.Method == i.Method && r.URL.Path == i.URL
})
Filtering sensitive information:
r, _ := recorder.New("fixtures/filtered")
r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
return nil
})
Getting Started
To use go-vcr in your project:
-
Install the library:
go get github.com/dnaeon/go-vcr/recorder
-
Import and use in your code:
import ( "github.com/dnaeon/go-vcr/recorder" "net/http" ) func main() { r, _ := recorder.New("fixtures/my_cassette") defer r.Stop() client := &http.Client{ Transport: r, } // Use the client to make HTTP requests // They will be recorded or played back }
Competitor Comparisons
Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.
Pros of vcr
- More mature and widely used in the Ruby ecosystem
- Extensive documentation and community support
- Supports multiple HTTP libraries and adapters
Cons of vcr
- Limited to Ruby language and ecosystem
- May require more setup and configuration for complex scenarios
Code Comparison
vcr:
VCR.use_cassette("example") do
response = Net::HTTP.get_response(URI('http://example.com'))
end
go-vcr:
recorder, err := vcr.New("fixtures/example")
defer recorder.Stop()
client := &http.Client{Transport: recorder}
resp, err := client.Get("http://example.com")
Key Differences
- Language: vcr is for Ruby, while go-vcr is for Go
- Integration: vcr integrates with various Ruby HTTP libraries, while go-vcr focuses on Go's net/http package
- Flexibility: vcr offers more configuration options and matchers, while go-vcr provides a simpler API
Use Cases
- vcr: Ideal for Ruby projects with complex HTTP interactions and testing requirements
- go-vcr: Well-suited for Go projects needing straightforward HTTP request recording and playback
Both libraries serve similar purposes in their respective ecosystems, offering developers tools to record and replay HTTP interactions for testing and development purposes.
Record, Replay, and Stub HTTP Interactions.
Pros of Pollyjs
- Cross-platform support (Node.js and browser)
- More extensive features, including request matching and response customization
- Active development and maintenance by Netflix
Cons of Pollyjs
- Larger codebase and potentially steeper learning curve
- JavaScript-specific, limiting use in other language ecosystems
Code Comparison
go-vcr:
r, err := recorder.New("fixtures/example")
defer r.Stop()
client := &http.Client{
Transport: r,
}
Pollyjs:
const polly = new Polly('example');
const { server } = polly;
server.get('/api').intercept((req, res) => {
res.status(200).json({ message: 'Hello World' });
});
Key Differences
- Language: go-vcr is written in Go, while Pollyjs is JavaScript-based
- Scope: go-vcr focuses on HTTP recording/playback, Pollyjs offers broader functionality
- Configuration: Pollyjs provides more granular control over request matching and response handling
- Ecosystem: go-vcr integrates well with Go testing frameworks, Pollyjs with JavaScript testing tools
Both libraries serve similar purposes but cater to different language ecosystems and offer varying levels of complexity and features. The choice between them largely depends on the project's language, requirements, and desired level of control over the recording and playback process.
HTTP server mocking and expectations library for Node.js
Pros of nock
- Widely adopted in the JavaScript ecosystem with extensive community support
- Offers powerful request matching capabilities, including regex and function-based matchers
- Provides built-in support for common HTTP scenarios like persistent mocks and delayed responses
Cons of nock
- Limited to Node.js environment, not suitable for browser-based applications
- Can be more complex to set up and maintain for large test suites
- Mocking at the HTTP level may not capture all nuances of real network interactions
Code Comparison
nock:
const nock = require('nock');
const scope = nock('https://api.example.com')
.get('/users')
.reply(200, { users: ['Alice', 'Bob'] });
go-vcr:
r, err := recorder.New("fixtures/users")
defer r.Stop()
client := &http.Client{Transport: r}
resp, err := client.Get("https://api.example.com/users")
Key Differences
- Language: nock is for JavaScript/Node.js, while go-vcr is for Go
- Approach: nock intercepts HTTP requests at runtime, go-vcr records and replays HTTP interactions
- Flexibility: nock offers more fine-grained control over mocked responses, while go-vcr focuses on simplicity and real response playback
Both libraries serve similar purposes but cater to different ecosystems and testing philosophies. nock is more suitable for detailed mocking scenarios, while go-vcr excels in recording and replaying actual HTTP interactions.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
go-vcr
go-vcr
simplifies testing by recording your HTTP interactions and replaying
them in future runs in order to provide fast, deterministic and accurate testing
of your code.
go-vcr
was inspired by the VCR library for Ruby.
Installation
Install go-vcr
by executing the command below:
$ go get -v gopkg.in/dnaeon/go-vcr.v4
Note, that if you are migrating from a previous version of go-vcr
, you may
need to re-create your tests cassettes.
Usage
A quick example of using go-vcr
.
package helloworld_test
import (
"testing"
"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
)
func TestHelloWorld(t *testing.T) {
// Create our recorder
r, err := recorder.New("fixtures/hello-world")
if err != nil {
t.Fatal(err)
}
defer r.Stop() // Make sure recorder is stopped once done with it
client := r.GetDefaultClient()
url := "https://go.dev/"
resp, err := client.Get(url)
if err != nil {
t.Fatalf("Failed to get url %s: %s", url, err)
}
t.Logf("GET %s: %d\n", url, resp.StatusCode)
}
Running this test code for the first time will result in creating the
fixtures/hello-world.yaml
cassette, which will contain the recorded HTTP
interaction between our HTTP client and the remote server.
When we execute this test next time, what would happen is that go-vcr
will
replay the already recorded HTTP interactions from the cassette, instead of
making actual external calls.
Please also check the examples directory from this repo for complete and ready to run examples.
You can also refer to the test cases for additional examples.
Custom Request Matching
During replay mode, you can customize the way incoming requests are matched
against the recorded request/response pairs by defining a recorder.MatcherFunc
function.
For example, the following matcher will match on method, URL and body:
func customMatcher(r *http.Request, i cassette.Request) bool {
if r.Body == nil || r.Body == http.NoBody {
return cassette.DefaultMatcher(r, i)
}
var reqBody []byte
var err error
reqBody, err = io.ReadAll(r.Body)
if err != nil {
log.Fatal("failed to read request body")
}
r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))
return r.Method == i.Method && r.URL.String() == i.URL && string(reqBody) == i.Body
}
...
// Recorder options
opts := []recorder.Option{
recorder.WithCassette("fixtures/matchers"),
recorder.WithMatcher(customMatcher),
}
rec, err := recorder.New(opts...)
if err != nil {
log.Fatal(err)
}
defer rec.Stop() // Make sure recorder is stopped once done with it
client := rec.GetDefaultClient()
resp, err := client.Get("https://www.google.com/")
...
Hooks
Hooks in go-vcr
are regular functions which take an HTTP interaction and are
invoked at different stages of the playback.
You can use hooks to modify a request/response before it is saved on disk, before it is returned to the client, or anything else that you might want to do with it, e.g. you might want to simply log each captured interaction.
You often provide sensitive data, such as API credentials, when making requests against a service.
By default, this data will be stored in the recorded data but you probably don't want this.
Removing or replacing data before it is stored can be done by adding one or more
Hook
s to your Recorder
.
There are different kinds of hooks, which are invoked in different stages of the
playback. The supported hook kinds are AfterCaptureHook
, BeforeSaveHook
,
BeforeResponseReplayHook
and OnRecorderStop
.
Here is an example that removes the Authorization
header from all requests
right after capturing a new interaction.
// A hook which removes Authorization headers from all requests
hook := func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
return nil
}
// Recorder options
opts := []recorder.Option{
recorder.WithCassette("fixtures/filters"),
recorder.WithHook(hook, recorder.AfterCaptureHook),
recorder.WithMatcher(cassette.NewDefaultMatcher(cassette.WithIgnoreAuthorization(true))),
}
r, err := recorder.New(opts...)
if err != nil {
log.Fatal(err)
}
defer r.Stop() // Make sure recorder is stopped once done with it
...
Hooks added using recorder.AfterCaptureHook
are applied right after an
interaction is captured and added to the in-memory cassette. This may not always
be what you need. For example if you modify an interaction using this hook kind
then subsequent test code will see the edited response.
For instance, if a response body contains an OAuth access token that is needed
for subsequent requests, then redacting the access token using a
AfterCaptureHook
will result in authorization failures in subsequent test
code.
In such cases you would want to modify the recorded interactions right before
they are saved on disk. For that purpose you should be using a BeforeSaveHook
,
e.g.
// Your test code will continue to see the real access token and
// it is redacted before the recorded interactions are saved on disk
hook := func(i *cassette.Interaction) error {
if strings.Contains(i.Request.URL, "/oauth/token") {
i.Response.Body = `{"access_token": "[REDACTED]"}`
}
return nil
}
// Recorder options
opts := []recorder.Option{
recorder.WithCassette("fixtures/filters"),
recorder.WithHook(hook, recorder.BeforeSaveHook),
}
r, err := recorder.New(opts...)
if err != nil {
log.Fatal(err)
}
defer r.Stop() // Make sure recorder is stopped once done with it
...
Passing Through Requests
Sometimes you want to allow specific requests to pass through to the remote server without recording anything.
Globally, you can use ModePassthrough
for this, but if you want to disable the
recorder for individual requests, you can add Passthrough
handlers to the
recorder.
Here's an example to pass through requests to a specific endpoint:
passthrough := func(req *http.Request) bool {
return req.URL.Path == "/login"
}
// Recorder options
opts := []recorder.Option{
recorder.WithCassette("fixtures/filters"),
recorder.WithPassthrough(passthrough),
}
r, err := recorder.New(opts...)
if err != nil {
log.Fatal(err)
}
defer r.Stop() // Make sure recorder is stopped once done with it
...
Server Side
VCR testing can also be used for creating server-side tests. Use the
recorder.HTTPMiddleware
with an HTTP handler in order to create fixtures from
incoming requests and the handler's responses. Then, these requests can be
replayed and compared against the recorded responses to create a regression
test.
Rather than mocking/recording external HTTP interactions, this will record and replay incoming interactions with your application's HTTP server.
See an example here.
License
go-vcr
is Open Source and licensed under the BSD
License
Top Related Projects
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot