{"id":13413882,"url":"https://github.com/dnaeon/go-vcr","last_synced_at":"2025-05-16T01:03:50.543Z","repository":{"id":40614218,"uuid":"47974873","full_name":"dnaeon/go-vcr","owner":"dnaeon","description":"Record and replay your HTTP interactions for fast, deterministic and accurate tests","archived":false,"fork":false,"pushed_at":"2025-04-12T19:28:28.000Z","size":591,"stargazers_count":1302,"open_issues_count":3,"forks_count":79,"subscribers_count":13,"default_branch":"v4","last_synced_at":"2025-05-08T22:18:10.140Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dnaeon.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.org","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["dnaeon"]}},"created_at":"2015-12-14T12:52:17.000Z","updated_at":"2025-05-08T08:26:22.000Z","dependencies_parsed_at":"2024-06-17T14:20:23.709Z","dependency_job_id":"5997cced-f858-4270-9324-85676f79c45a","html_url":"https://github.com/dnaeon/go-vcr","commit_stats":{"total_commits":260,"total_committers":41,"mean_commits":6.341463414634147,"dds":0.2846153846153846,"last_synced_commit":"e0eabf67b1364a47a61af2dc89b4146a5dd8afbf"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnaeon%2Fgo-vcr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnaeon%2Fgo-vcr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnaeon%2Fgo-vcr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnaeon%2Fgo-vcr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dnaeon","download_url":"https://codeload.github.com/dnaeon/go-vcr/tar.gz/refs/heads/v4","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253155004,"owners_count":21862625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:51.887Z","updated_at":"2025-05-08T22:18:15.318Z","avatar_url":"https://github.com/dnaeon.png","language":"Go","funding_links":["https://github.com/sponsors/dnaeon"],"categories":["Go","Testing","测试","Template Engines","\u003cspan id=\"测试-testing\"\u003e测试 Testing\u003c/span\u003e","测试相关","测试相关`测试库和测试数据集生成库`","Testing Frameworks"],"sub_categories":["HTTP客户端","Advanced Console UIs","Testing Frameworks","HTTP Clients","交流","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","查询语"],"readme":"## go-vcr\n\n[![Build Status](https://github.com/dnaeon/go-vcr/actions/workflows/test.yaml/badge.svg)](https://github.com/dnaeon/go-vcr/actions/workflows/test.yaml/badge.svg)\n[![Go Reference](https://pkg.go.dev/badge/gopkg.in/dnaeon/go-vcr.v4.svg)](https://pkg.go.dev/gopkg.in/dnaeon/go-vcr.v4)\n[![Go Report Card](https://goreportcard.com/badge/gopkg.in/dnaeon/go-vcr.v4)](https://goreportcard.com/report/gopkg.in/dnaeon/go-vcr.v4)\n[![codecov](https://codecov.io/gh/dnaeon/go-vcr/branch/v4/graph/badge.svg)](https://codecov.io/gh/dnaeon/go-vcr)\n\n`go-vcr` simplifies testing by recording your HTTP interactions and replaying\nthem in future runs in order to provide fast, deterministic and accurate testing\nof your code.\n\n`go-vcr` was inspired by the [VCR library for Ruby](https://github.com/vcr/vcr).\n\n## Installation\n\nInstall `go-vcr` by executing the command below:\n\n```bash\n$ go get -v gopkg.in/dnaeon/go-vcr.v4\n```\n\nNote, that if you are migrating from a previous version of `go-vcr`, you may\nneed to re-create your tests cassettes.\n\n## Usage\n\nA quick example of using `go-vcr`.\n\n``` go\npackage helloworld_test\n\nimport (\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder\"\n)\n\nfunc TestHelloWorld(t *testing.T) {\n\t// Create our recorder\n\tr, err := recorder.New(filepath.Join(\"testdata\", strings.ReplaceAll(t.Name(), \"/\", \"_\")))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(func() {\n\t\t// Make sure recorder is stopped once done with it.\n\t\tif err := r.Stop(); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t})\n\n\tclient := r.GetDefaultClient()\n\turl := \"https://go.dev/VERSION?m=text\"\n\n\tresp, err := client.Get(url)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to get url %s: %s\", url, err)\n\t}\n\n\tt.Logf(\"GET %s: %d\\n\", url, resp.StatusCode)\n}\n```\n\nRunning this test code for the first time will result in creating the\n`testdata/TestHelloWorld.yaml` cassette, which will contain the recorded HTTP\ninteraction between our HTTP client and the remote server.\n\nWhen we execute this test next time, what would happen is that `go-vcr` will\nreplay the already recorded HTTP interactions from the cassette, instead of\nmaking actual external calls.\n\nPlease also check the [examples](./examples) directory from this repo for\ncomplete and ready to run examples.\n\nYou can also refer to the [test cases](./pkg/recorder/recorder_test.go) for\nadditional examples.\n\n## Custom Request Matching\n\nDuring replay mode, you can customize the way incoming requests are matched\nagainst the recorded request/response pairs by defining a `recorder.MatcherFunc`\nfunction.\n\nFor example, the following matcher will match on method, URL and body:\n\n``` go\n\nfunc customMatcher(r *http.Request, i cassette.Request) bool {\n\tif r.Body == nil || r.Body == http.NoBody {\n\t\treturn cassette.DefaultMatcher(r, i)\n\t}\n\n\tvar reqBody []byte\n\tvar err error\n\treqBody, err = io.ReadAll(r.Body)\n\tif err != nil {\n\t\tlog.Fatal(\"failed to read request body\")\n\t}\n\tr.Body.Close()\n\tr.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))\n\n\treturn r.Method == i.Method \u0026\u0026 r.URL.String() == i.URL \u0026\u0026 string(reqBody) == i.Body\n}\n\n...\n\n// Recorder options\nopts := []recorder.Option{\n\trecorder.WithMatcher(customMatcher),\n}\n\nrec, err := recorder.New(\"testdata/matchers\", opts...)\nif err != nil {\n        log.Fatal(err)\n}\ndefer rec.Stop() // Make sure recorder is stopped once done with it\n\nclient := rec.GetDefaultClient()\nresp, err := client.Get(\"https://www.google.com/\")\n\n...\n```\n\n## Hooks\n\nHooks in `go-vcr` are regular functions which take an HTTP interaction and are\ninvoked at different stages of the playback.\n\nYou can use hooks to modify a request/response before it is saved on disk,\nbefore it is returned to the client, or anything else that you might want to do\nwith it, e.g. you might want to simply log each captured interaction.\n\nYou often provide sensitive data, such as API credentials, when making requests\nagainst a service.\n\nBy default, this data will be stored in the recorded data but you probably don't\nwant this.\n\nRemoving or replacing data before it is stored can be done by adding one or more\n`Hook`s to your `Recorder`.\n\nThere are different kinds of hooks, which are invoked in different stages of the\nplayback. The supported hook kinds are `AfterCaptureHook`, `BeforeSaveHook`,\n`BeforeResponseReplayHook` and `OnRecorderStop`.\n\nHere is an example that removes the `Authorization` header from all requests\nright after capturing a new interaction.\n\n``` go\n// A hook which removes Authorization headers from all requests\nhook := func(i *cassette.Interaction) error {\n\tdelete(i.Request.Headers, \"Authorization\")\n\treturn nil\n}\n\n// Recorder options\nopts := []recorder.Option{\n\trecorder.WithHook(hook, recorder.AfterCaptureHook),\n\trecorder.WithMatcher(cassette.NewDefaultMatcher(cassette.WithIgnoreAuthorization())),\n}\n\nr, err := recorder.New(\"testdata/filters\", opts...)\nif err != nil {\n\tlog.Fatal(err)\n}\ndefer r.Stop() // Make sure recorder is stopped once done with it\n\n...\n```\n\nHooks added using `recorder.AfterCaptureHook` are applied right after an\ninteraction is captured and added to the in-memory cassette. This may not always\nbe what you need. For example if you modify an interaction using this hook kind\nthen subsequent test code will see the edited response.\n\nFor instance, if a response body contains an OAuth access token that is needed\nfor subsequent requests, then redacting the access token using a\n`AfterCaptureHook` will result in authorization failures in subsequent test\ncode.\n\nIn such cases you would want to modify the recorded interactions right before\nthey are saved on disk. For that purpose you should be using a `BeforeSaveHook`,\ne.g.\n\n``` go\n// Your test code will continue to see the real access token and\n// it is redacted before the recorded interactions are saved on disk\nhook := func(i *cassette.Interaction) error {\n\tif strings.Contains(i.Request.URL, \"/oauth/token\") {\n\t\ti.Response.Body = `{\"access_token\": \"[REDACTED]\"}`\n\t}\n\n\treturn nil\n}\n\n// Recorder options\nopts := []recorder.Option{\n\trecorder.WithHook(hook, recorder.BeforeSaveHook),\n}\n\nr, err := recorder.New(\"testdata/filters\", opts...)\nif err != nil {\n\tlog.Fatal(err)\n}\ndefer r.Stop() // Make sure recorder is stopped once done with it\n\n...\n```\n\n## Passing Through Requests\n\nSometimes you want to allow specific requests to pass through to the remote\nserver without recording anything.\n\nGlobally, you can use `ModePassthrough` for this, but if you want to disable the\nrecorder for individual requests, you can add `Passthrough` handlers to the\nrecorder.\n\nHere's an example to pass through requests to a specific endpoint:\n\n``` go\npassthrough := func(req *http.Request) bool {\n\treturn req.URL.Path == \"/login\"\n}\n\n// Recorder options\nopts := []recorder.Option{\n\trecorder.WithPassthrough(passthrough),\n}\n\nr, err := recorder.New(\"testdata/filters\", opts...)\nif err != nil {\n\tlog.Fatal(err)\n}\ndefer r.Stop() // Make sure recorder is stopped once done with it\n\n...\n```\n\n## Server Side\n\nVCR testing can also be used for creating server-side tests. Use the\n`recorder.HTTPMiddleware` with an HTTP handler in order to create fixtures from\nincoming requests and the handler's responses. Then, these requests can be\nreplayed and compared against the recorded responses to create a regression\ntest.\n\nRather than mocking/recording external HTTP interactions, this will record and\nreplay _incoming_ interactions with your application's HTTP server.\n\nSee [an example here](./examples/middleware_test.go).\n\n## License\n\n`go-vcr` is Open Source and licensed under the [BSD\nLicense](http://opensource.org/licenses/BSD-2-Clause)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnaeon%2Fgo-vcr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdnaeon%2Fgo-vcr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnaeon%2Fgo-vcr/lists"}