Documentation ¶
Overview ¶
Package dvr attempts to make testing far easier by allowing calls to remote HTTP services to be captured and replayed when running the tests a second time.
In recording mode (-dvr.record) each request will be captured and recorded to a file (-dvr.file, which defaults to testdata/archive.dvr). In replay mode each request will be matched against of the requests in the archive. This ensures that a unit test can remove all dependencies on remote services while running, which is ideal for most testing environments.
Note that this library works be replaying net.http's DefaultTransport with one that will intercept queries. If you are using a custom client, or replacing the http.DefaultTransport you may need to sub a RoundTripper from this package in place.
All common error types will be preserved and returned via the archive, however some types can not be restored due to the way that gob works. In these cases an error will be returned that satisfies the error interface but it will be a different type.
When in replay mode requests are matched if all of the following are the same: URL, Body, Headers, Trailers. If all of these elements are the same as a recorded request then the response will be returned to the client. If no request matches then the test will panic since re-requesting may cause all sorts of issues. If this matching strategy is not sufficient then you can make value Match() contain a function that can parse two requests and establish if they are the same.
This library is intended to be user during unit testing so much of its design is wrapped around this, and while it can be used outside of unit tests it is strongly not recommended.
Example (BasicUsage) ¶
For the most basic of use cases you need only ensure that the dvr library is included in your test file. Including it with an underscore, like in this example, is all that is necessary to get it in place.
package main import ( "net/http" "net/url" "testing" _ "github.com/orchestrate-io/dvr" ) // When running this test you can run it in one of three modes: // // Pass through: In this mode nothing is recorded or replayed. // // go test . // // Recording: In this mode all HTTP calls are recorded into a file (the default // // is testdata/archive.dvr, and is control by -dvr.file) // go test -dvr.record . // // Replay: In this mode all HTTP calls are replayed from the recording file // // captured in Recording mode above. // go test -dvr.replay . // // - Note that this function needs the leading underscore removed in order // to work in the real world. It exists purely because of the rules around // "playable" document examples. func _TestCallToApi(t *testing.T) { var err error req := &http.Request{} req.URL, err = url.Parse("http://golang.org") resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("Error from net.http: %s", err) } else if resp.StatusCode != 200 { t.Fatalf("Bad status code: %d", resp.StatusCode) } } // For the most basic of use cases you need only ensure that the dvr library // is included in your test file. Including it with an underscore, like in // this example, is all that is necessary to get it in place. func main() { // Though this library can be used outside of golang's testing library // its not recommended. This main() exists only to make the example // render in godoc. }
Output:
Index ¶
Examples ¶
Constants ¶
const InterceptorToken = "dvr_gzipper_token_a9s87d9aish2"
This token allows us to intercept startup and therefor act as a command line "gzip" process. If os.Argv[1] is this value then we simply take os.Stdin and gzip it into os.Stdout.
This is necessary since Go's test library doesn't have any way of indicating or calling out when all the tests have finished which means that it is impossible to close a gzip file.
Variables ¶
var ( // If this is set to true then -dvr.replay becomes default if not // other flags are provided. If this is falls then the default will be // to pass queries through without recording or replaying them DefaultReplay bool )
var DefaultRoundTripper http.RoundTripper
This is the round tripper that replaced the default round tripper in the net/http library. Its stored here so it can be retrieved if lost.
var Matcher func(left, right *RequestResponse) bool
This function is used by the replay component of this library to determine if an incoming request matches a request from the archive. If this function returns true then the requests are deemed to have "matched". Note that mutation of either object (other than the UserData field) will likely result in a panic or crash. The left object will not have Response* fields populated.
The default matcher will match a request if it Request's URL, Body, Headers and Trailers are all the same.
var Obfuscator func(*RequestResponse)
If this value is anything other than nil it will be called on a copy of the passed in *http.Request and a copy of the returned *http.Response object. Any mutations to these objects will be stored in the archive, but NOT be altered in the recording unit test. The intention of this function is to allow obfuscation of data you do not want recorded.
An example usage of this function is to change the password used to authenticate against a web service in order to allow any user to run the test. See the "RequestObfuscation" example for details.
var OriginalDefaultTransport http.RoundTripper
This is the round tripper that we overwrote in net/http. It is preserved here in case it needs be recovered.
Functions ¶
func BasicAuthObfuscator ¶
func BasicAuthObfuscator(username, password string) func(*RequestResponse)
This function call will return a function that can act as a Obfuscator which removes any HTTP Basic Auth credential and replaces it with the given arguments. The results of this call can be directly used with the Obfuscator variable.
func IsPassingThrough ¶
func IsPassingThrough() bool
Returns true if the DVR library is in pass through mode.
func NewRoundTripper ¶
func NewRoundTripper(fallback http.RoundTripper) http.RoundTripper
This creates a new RoundTripper object with the given RoundTripper object as its fall back (for pass through and recording modes).
func RegisterErrorType ¶
func RegisterErrorType(err error)
Adds an error interface object to the list of known types that this library will be able to encode. This is necessary due to the way that gob encodes interface object. The only error types here are those that will be returned from the RoundTripper object. Typical use cases should not need this at all. If you are using this you must do it via your modules init() otherwise results can be unpredictable.
Types ¶
type RequestResponse ¶
type RequestResponse struct { // This is the Request object that was passed in to the RoundTripper // call. This object will have it's Body field set to nil, with the body // being represented in the byte array 'RequestBody'. The error (if any) // returned when reading from Body is stored in RequestBodyError. Request *http.Request RequestBody []byte RequestBodyError error // This is the Response object that was returned to the caller. Note that // Body in this field is saved in the ResponseBody field, and the error // returned from the server is stored in ResponseBodyError. Response *http.Response ResponseBody []byte ResponseBodyError error // This is the error returned from the RountTrip() call. Error error // This stores any user data that is necessary for the Matcher() function. UserData interface{} }
This structure stores information about a Request/Response pairing. It is intended to keep the data together for use when matching and such.