Categorygithub.com/h2non/gock
modulepackage
1.2.0
Repository: https://github.com/h2non/gock.git
Documentation: pkg.go.dev

# README

gock Build Status GitHub release GoDoc Coverage Status Go Report Card license

Versatile HTTP mocking made easy in Go that works with any net/http based stdlib implementation.

Heavily inspired by nock. There is also its Python port, pook.

To get started, take a look to the examples.

Features

  • Simple, expressive, fluent API.
  • Semantic API DSL for declarative HTTP mock declarations.
  • Built-in helpers for easy JSON/XML mocking.
  • Supports persistent and volatile TTL-limited mocks.
  • Full regular expressions capable HTTP request mock matching.
  • Designed for both testing and runtime scenarios.
  • Match request by method, URL params, headers and bodies.
  • Extensible and pluggable HTTP matching rules.
  • Ability to switch between mock and real networking modes.
  • Ability to filter/map HTTP requests for accurate mock matching.
  • Supports map and filters to handle mocks easily.
  • Wide compatible HTTP interceptor using http.RoundTripper interface.
  • Works with any net/http compatible client, such as gentleman.
  • Network timeout/cancelation delay simulation.
  • Extensible and hackable API.
  • Dependency free.

Installation

go get -u github.com/h2non/gock

API

See godoc reference for detailed API documentation.

How it mocks

  1. Intercepts any HTTP outgoing request via http.DefaultTransport or custom http.Transport used by any http.Client.
  2. Matches outgoing HTTP requests against a pool of defined HTTP mock expectations in FIFO declaration order.
  3. If at least one mock matches, it will be used in order to compose the mock HTTP response.
  4. 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.

Tips

Testing

Declare your mocks before you start declaring the concrete test logic:

func TestFoo(t *testing.T) {
  defer gock.Off() // Flush pending mocks after test execution

  gock.New("http://server.com").
    Get("/bar").
    Reply(200).
    JSON(map[string]string{"foo": "bar"})

  // Your test code starts here...
}

Race conditions

If you're running concurrent code, be aware that your mocks are declared first to avoid unexpected race conditions while configuring gock or intercepting custom HTTP clients.

gock is not fully thread-safe, but sensible parts are. Any help making gock more reliable in this sense is appreciated.

Define complex mocks first

If you're mocking a bunch of mocks in the same test suite, it's recommended to define the more concrete mocks first, and then the generic ones.

This approach usually avoids matching unexpected generic mocks (e.g: specific header, body payload...) instead of the generic ones that performs less complex matches.

Disable gock traffic interception once done

In other to minimize potential side effects within your test code, it's a good practice disabling gock once you are done with your HTTP testing logic.

A Go idiomatic approach for doing this can be using it in a defer statement, such as:

func TestGock (t *testing.T) {
	defer gock.Off()

	// ... my test code goes here
}

Intercept an http.Client just once

You don't need to intercept multiple times the same http.Client instance.

Just call gock.InterceptClient(client) once, typically at the beginning of your test scenarios.

Restore an http.Client after interception

NOTE: this is not required is you are using http.DefaultClient or http.DefaultTransport.

As a good testing pattern, you should call gock.RestoreClient(client) after running your test scenario, typically as after clean up hook.

You can also use a defer statement for doing it, as you do with gock.Off(), such as:

func TestGock (t *testing.T) {
	defer gock.Off()
	defer gock.RestoreClient(client)

	// ... my test code goes here
}

Examples

See examples directory for more featured use cases.

Simple mocking via tests

package test

import (
  "io/ioutil"
  "net/http"
  "testing"

  "github.com/nbio/st"
  "github.com/h2non/gock"
)

func TestSimple(t *testing.T) {
  defer gock.Off()

  gock.New("http://foo.com").
    Get("/bar").
    Reply(200).
    JSON(map[string]string{"foo": "bar"})

  res, err := http.Get("http://foo.com/bar")
  st.Expect(t, err, nil)
  st.Expect(t, res.StatusCode, 200)

  body, _ := ioutil.ReadAll(res.Body)
  st.Expect(t, string(body)[:13], `{"foo":"bar"}`)

  // Verify that we don't have pending mocks
  st.Expect(t, gock.IsDone(), true)
}

Request headers matching

package test

import (
  "io/ioutil"
  "net/http"
  "testing"

  "github.com/nbio/st"
  "github.com/h2non/gock"
)

func TestMatchHeaders(t *testing.T) {
  defer gock.Off()

  gock.New("http://foo.com").
    MatchHeader("Authorization", "^foo bar$").
    MatchHeader("API", "1.[0-9]+").
    HeaderPresent("Accept").
    Reply(200).
    BodyString("foo foo")

  req, err := http.NewRequest("GET", "http://foo.com", nil)
  req.Header.Set("Authorization", "foo bar")
  req.Header.Set("API", "1.0")
  req.Header.Set("Accept", "text/plain")

  res, err := (&http.Client{}).Do(req)
  st.Expect(t, err, nil)
  st.Expect(t, res.StatusCode, 200)
  body, _ := ioutil.ReadAll(res.Body)
  st.Expect(t, string(body), "foo foo")

  // Verify that we don't have pending mocks
  st.Expect(t, gock.IsDone(), true)
}

Request param matching

package test

import (
  "io/ioutil"
  "net/http"
  "testing"

  "github.com/nbio/st"
  "github.com/h2non/gock"
)

func TestMatchParams(t *testing.T) {
  defer gock.Off()

  gock.New("http://foo.com").
    MatchParam("page", "1").
    MatchParam("per_page", "10").
    Reply(200).
    BodyString("foo foo")

  req, err := http.NewRequest("GET", "http://foo.com?page=1&per_page=10", nil)

  res, err := (&http.Client{}).Do(req)
  st.Expect(t, err, nil)
  st.Expect(t, res.StatusCode, 200)
  body, _ := ioutil.ReadAll(res.Body)
  st.Expect(t, string(body), "foo foo")

  // Verify that we don't have pending mocks
  st.Expect(t, gock.IsDone(), true)
}

JSON body matching and response

package test

import (
  "bytes"
  "io/ioutil"
  "net/http"
  "testing"
	
	"github.com/nbio/st"
  "github.com/h2non/gock"
)

func TestMockSimple(t *testing.T) {
  defer gock.Off()

  gock.New("http://foo.com").
    Post("/bar").
    MatchType("json").
    JSON(map[string]string{"foo": "bar"}).
    Reply(201).
    JSON(map[string]string{"bar": "foo"})

  body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
  res, err := http.Post("http://foo.com/bar", "application/json", body)
  st.Expect(t, err, nil)
  st.Expect(t, res.StatusCode, 201)

  resBody, _ := ioutil.ReadAll(res.Body)
  st.Expect(t, string(resBody)[:13], `{"bar":"foo"}`)

  // Verify that we don't have pending mocks
  st.Expect(t, gock.IsDone(), true)
}

Mocking a custom http.Client and http.RoundTripper

package test

import (
  "io/ioutil"
  "net/http"
  "testing"

  "github.com/nbio/st"
  "github.com/h2non/gock"
)

func TestClient(t *testing.T) {
  defer gock.Off()

  gock.New("http://foo.com").
    Reply(200).
    BodyString("foo foo")

  req, err := http.NewRequest("GET", "http://foo.com", nil)
  client := &http.Client{Transport: &http.Transport{}}
  gock.InterceptClient(client)

  res, err := client.Do(req)
  st.Expect(t, err, nil)
  st.Expect(t, res.StatusCode, 200)
  body, _ := ioutil.ReadAll(res.Body)
  st.Expect(t, string(body), "foo foo")

  // Verify that we don't have pending mocks
  st.Expect(t, gock.IsDone(), true)
}

Enable real networking

package main

import (
  "fmt"
  "io/ioutil"
  "net/http"

  "github.com/h2non/gock"
)

func main() {
  defer gock.Off()
  defer gock.DisableNetworking()

  gock.EnableNetworking()
  gock.New("http://httpbin.org").
    Get("/get").
    Reply(201).
    SetHeader("Server", "gock")

  res, err := http.Get("http://httpbin.org/get")
  if err != nil {
    fmt.Errorf("Error: %s", err)
  }

  // The response status comes from the mock
  fmt.Printf("Status: %d\n", res.StatusCode)
  // The server header comes from mock as well
  fmt.Printf("Server header: %s\n", res.Header.Get("Server"))
  // Response body is the original
  body, _ := ioutil.ReadAll(res.Body)
  fmt.Printf("Body: %s", string(body))
}

Debug intercepted http requests

package main

import (
	"bytes"
	"net/http"
	
  "github.com/h2non/gock"
)

func main() {
	defer gock.Off()
	gock.Observe(gock.DumpRequest)

	gock.New("http://foo.com").
		Post("/bar").
		MatchType("json").
		JSON(map[string]string{"foo": "bar"}).
		Reply(200)

	body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
	http.Post("http://foo.com/bar", "application/json", body)
}

Hacking it!

You can easily hack gock defining custom matcher functions with own matching rules.

See add matcher functions and custom matching layer examples for further details.

License

MIT - Tomas Aparicio

# Functions

Clean cleans the mocks store removing disabled or obsolete mocks.
CleanUnmatchedRequest cleans the unmatched requests internal registry.
Disable disables HTTP traffic interception by gock.
DisableNetworking disables real HTTP networking.
DisableNetworkingFilters disables registered networking filters.
EnableNetworking enables real HTTP networking.
Exists checks if the given Mock is already registered.
Flush flushes the current stack of registered mocks.
GetAll returns the current stack of registered mocks.
GetUnmatchedRequests returns all requests that have been received but haven't matched any mock.
HasUnmatchedRequest returns true if gock has received any requests that didn't match a mock.
Intercept enables HTTP traffic interception via http.DefaultTransport.
InterceptClient allows the developer to intercept HTTP traffic using a custom http.Client who uses a non default http.Transport/http.RoundTripper implementation.
Intercepting returns true if gock is currently able to intercept.
IsDone returns true if all the registered mocks has been triggered successfully.
IsPending returns true if there are pending mocks.
MatchBody tries to match the request body.
MatchHeaders matches the headers fields of the given request.
MatchHost matches the HTTP host header field of the given request.
MatchMethod matches the HTTP method of the given request.
MatchMock is a helper function that matches the given http.Request in the list of registered mocks, returning it if matches or error if it fails.
MatchPath matches the HTTP URL path of the given request.
MatchPathParams matches the URL path parameters of the given request.
MatchQueryParams matches the URL query params fields of the given request.
MatchScheme matches the request URL protocol scheme.
NetworkingFilter determines if an http.Request should be triggered or not.
New creates and registers a new HTTP mock with default settings and returns the Request DSL for HTTP mock definition and set up.
NewBasicMatcher creates a new matcher with header only mock matchers.
NewEmptyMatcher creates a new empty matcher without default matchers.
NewMatcher creates a new mock matcher using the default matcher functions.
NewMock creates a new HTTP mock based on the given request and response instances.
NewRequest creates a new Request instance.
NewResponse creates a new Response.
NewTransport creates a new *Transport with no responders.
Observe provides a hook to support inspection of the request and matched mock.
Off disables the default HTTP interceptors and removes all the registered mocks, even if they has not been intercepted yet.
OffAll is like `Off()`, but it also removes the unmatched requests registry.
Pending returns an slice of pending mocks.
Register registers a new mock in the current mocks stack.
Remove removes a registered mock by reference.
Responder builds a mock http.Response based on the given Response mock.
RestoreClient allows the developer to disable and restore the original transport in the given http.Client.

# Constants

EOL represents the end of line character.
Version defines the current package semantic version.

# Variables

BodyTypeAliases stores a generic MIME type by alias.
BodyTypes stores the supported MIME body types for matching.
CompressionSchemes stores the supported Content-Encoding types for decompression.
DefaultMatcher stores the default Matcher instance used to match mocks.
DefaultTransport stores the default mock transport used by gock.
DumpRequest is a default implementation of ObserverFunc that dumps the HTTP/1.x wire representation of the http request.
ErrCannotMatch store the error returned in case of no matches.
Matchers stores all the built-in mock matchers.
MatchersBody exposes an slice of HTTP body specific built-in mock matchers.
MatchersHeader exposes an slice of HTTP header specific mock matchers.
NativeTransport stores the native net/http default transport in order to restore it when needed.

# Structs

Mocker implements a Mock capable interface providing a default mock configuration used internally to store mocks.
MockMatcher implements a mock matcher.
Options represents customized option for gock.
Request represents the high-level HTTP request used to store request fields used to match intercepted requests.
Response represents high-level HTTP fields to configure and define HTTP responses intercepted by gock.
Transport implements http.RoundTripper, which fulfills single http requests issued by an http.Client.

# Interfaces

Matcher represents the required interface implemented by mock matchers.
Mock represents the required interface that must be implemented by HTTP mock instances.

# Type aliases

FilterRequestFunc represents the required function interface for request filters.
FilterResponseFunc represents the required function interface impletemed by response filters.
MapRequestFunc represents the required function interface for request mappers.
MapResponseFunc represents the required function interface impletemed by response mappers.
MatchFunc represents the required function interface implemented by matchers.
ObserverFunc is implemented by users to inspect the outgoing intercepted HTTP traffic.