httpmock

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2023 License: MIT Imports: 21 Imported by: 192

README

httpmock Build Status Coverage Status GoDoc Version Mentioned in Awesome Go

Easy mocking of http responses from external resources.

Install

Currently supports Go 1.13 to 1.21 and is regularly tested against tip.

v1 branch has to be used instead of master.

In your go files, simply use:

import "github.com/jarcoal/httpmock"

Then next go mod tidy or go test invocation will automatically populate your go.mod with the latest httpmock release, now Version.

Usage

Simple Example:
func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  // Exact URL match
  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles",
    httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`))

  // Regexp match (could use httpmock.RegisterRegexpResponder instead)
  httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/\d+\z`,
    httpmock.NewStringResponder(200, `{"id": 1, "name": "My Great Article"}`))

  // do stuff that makes a request to articles
  ...

  // get count info
  httpmock.GetTotalCallCount()

  // get the amount of calls for the registered responder
  info := httpmock.GetCallCountInfo()
  info["GET https://api.mybiz.com/articles"] // number of GET calls made to https://api.mybiz.com/articles
  info["GET https://api.mybiz.com/articles/id/12"] // number of GET calls made to https://api.mybiz.com/articles/id/12
  info[`GET =~^https://api\.mybiz\.com/articles/id/\d+\z`] // number of GET calls made to https://api.mybiz.com/articles/id/<any-number>
}
Advanced Example:
func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  // our database of articles
  articles := make([]map[string]interface{}, 0)

  // mock to list out the articles
  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles",
    func(req *http.Request) (*http.Response, error) {
      resp, err := httpmock.NewJsonResponse(200, articles)
      if err != nil {
        return httpmock.NewStringResponse(500, ""), nil
      }
      return resp, nil
    })

  // return an article related to the request with the help of regexp submatch (\d+)
  httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/(\d+)\z`,
    func(req *http.Request) (*http.Response, error) {
      // Get ID from request
      id := httpmock.MustGetSubmatchAsUint(req, 1) // 1=first regexp submatch
      return httpmock.NewJsonResponse(200, map[string]interface{}{
        "id":   id,
        "name": "My Great Article",
      })
    })

  // mock to add a new article
  httpmock.RegisterResponder("POST", "https://api.mybiz.com/articles",
    func(req *http.Request) (*http.Response, error) {
      article := make(map[string]interface{})
      if err := json.NewDecoder(req.Body).Decode(&article); err != nil {
        return httpmock.NewStringResponse(400, ""), nil
      }

      articles = append(articles, article)

      resp, err := httpmock.NewJsonResponse(200, article)
      if err != nil {
        return httpmock.NewStringResponse(500, ""), nil
      }
      return resp, nil
    })

  // mock to add a specific article, send a Bad Request response
  // when the request body contains `"type":"toy"`
  httpmock.RegisterMatcherResponder("POST", "https://api.mybiz.com/articles",
    httpmock.BodyContainsString(`"type":"toy"`),
    httpmock.NewStringResponder(400, `{"reason":"Invalid article type"}`))

  // do stuff that adds and checks articles
}
Algorithm

When GET http://example.tld/some/path?b=12&a=foo&a=bar request is caught, all standard responders are checked against the following URL or paths, the first match stops the search:

  1. http://example.tld/some/path?b=12&a=foo&a=bar (original URL)
  2. http://example.tld/some/path?a=bar&a=foo&b=12 (sorted query params)
  3. http://example.tld/some/path (without query params)
  4. /some/path?b=12&a=foo&a=bar (original URL without scheme and host)
  5. /some/path?a=bar&a=foo&b=12 (same, but sorted query params)
  6. /some/path (path only)

If no standard responder matched, the regexp responders are checked, in the same order, the first match stops the search.

go-testdeep + tdsuite example:
// article_test.go

import (
  "testing"

  "github.com/jarcoal/httpmock"
  "github.com/maxatome/go-testdeep/helpers/tdsuite"
  "github.com/maxatome/go-testdeep/td"
)

type MySuite struct{}

func (s *MySuite) Setup(t *td.T) error {
  // block all HTTP requests
  httpmock.Activate()
  return nil
}

func (s *MySuite) PostTest(t *td.T, testName string) error {
  // remove any mocks after each test
  httpmock.Reset()
  return nil
}

func (s *MySuite) Destroy(t *td.T) error {
  httpmock.DeactivateAndReset()
  return nil
}

func TestMySuite(t *testing.T) {
  tdsuite.Run(t, &MySuite{})
}

func (s *MySuite) TestArticles(assert, require *td.T) {
  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles.json",
    httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`))

  // do stuff that makes a request to articles.json
}
Ginkgo example:
// article_suite_test.go

import (
  // ...
  "github.com/jarcoal/httpmock"
)
// ...
var _ = BeforeSuite(func() {
  // block all HTTP requests
  httpmock.Activate()
})

var _ = BeforeEach(func() {
  // remove any mocks
  httpmock.Reset()
})

var _ = AfterSuite(func() {
  httpmock.DeactivateAndReset()
})


// article_test.go

import (
  // ...
  "github.com/jarcoal/httpmock"
)

var _ = Describe("Articles", func() {
  It("returns a list of articles", func() {
    httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles.json",
      httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`))

    // do stuff that makes a request to articles.json
  })
})
Ginkgo + Resty Example:
// article_suite_test.go

import (
  // ...
  "github.com/jarcoal/httpmock"
  "github.com/go-resty/resty"
)
// ...
var _ = BeforeSuite(func() {
  // block all HTTP requests
  httpmock.ActivateNonDefault(resty.DefaultClient.GetClient())
})

var _ = BeforeEach(func() {
  // remove any mocks
  httpmock.Reset()
})

var _ = AfterSuite(func() {
  httpmock.DeactivateAndReset()
})


// article_test.go

import (
  // ...
  "github.com/jarcoal/httpmock"
  "github.com/go-resty/resty"
)

var _ = Describe("Articles", func() {
  It("returns a list of articles", func() {
    fixture := `{"status":{"message": "Your message", "code": 200}}`
    responder := httpmock.NewStringResponder(200, fixture)
    fakeUrl := "https://api.mybiz.com/articles.json"
    httpmock.RegisterResponder("GET", fakeUrl, responder)

    // fetch the article into struct
    articleObject := &models.Article{}
    _, err := resty.R().SetResult(articleObject).Get(fakeUrl)

    // do stuff with the article object ...
  })
})

Documentation

Overview

Package httpmock provides tools for mocking HTTP responses.

Simple Example:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  // Exact URL match
  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles",
    httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`))

  // Regexp match (could use httpmock.RegisterRegexpResponder instead)
  httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/\d+\z`,
    httpmock.NewStringResponder(200, `{"id": 1, "name": "My Great Article"}`))

  // do stuff that makes a request to articles

  // get count info
  httpmock.GetTotalCallCount()

  // get the amount of calls for the registered responder
  info := httpmock.GetCallCountInfo()
  info["GET https://api.mybiz.com/articles"]             // number of GET calls made to https://api.mybiz.com/articles
  info["GET https://api.mybiz.com/articles/id/12"]       // number of GET calls made to https://api.mybiz.com/articles/id/12
  info[`GET =~^https://api\.mybiz\.com/articles/id/\d+\z`] // number of GET calls made to https://api.mybiz.com/articles/id/<any-number>
}

Advanced Example:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  // our database of articles
  articles := make([]map[string]any, 0)

  // mock to list out the articles
  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles",
    func(req *http.Request) (*http.Response, error) {
      resp, err := httpmock.NewJsonResponse(200, articles)
      if err != nil {
        return httpmock.NewStringResponse(500, ""), nil
      }
      return resp, nil
    },
  )

  // return an article related to the request with the help of regexp submatch (\d+)
  httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/(\d+)\z`,
    func(req *http.Request) (*http.Response, error) {
      // Get ID from request
      id := httpmock.MustGetSubmatchAsUint(req, 1) // 1=first regexp submatch
      return httpmock.NewJsonResponse(200, map[string]any{
        "id":   id,
        "name": "My Great Article",
      })
    },
  )

  // mock to add a new article
  httpmock.RegisterResponder("POST", "https://api.mybiz.com/articles",
    func(req *http.Request) (*http.Response, error) {
      article := make(map[string]any)
      if err := json.NewDecoder(req.Body).Decode(&article); err != nil {
        return httpmock.NewStringResponse(400, ""), nil
      }

      articles = append(articles, article)

      resp, err := httpmock.NewJsonResponse(200, article)
      if err != nil {
        return httpmock.NewStringResponse(500, ""), nil
      }
      return resp, nil
    },
  )

  // do stuff that adds and checks articles
}

Index

Constants

This section is empty.

Variables

View Source
var DefaultTransport = NewMockTransport()

DefaultTransport is the default mock transport used by Activate, Deactivate, Reset, DeactivateAndReset, RegisterResponder, RegisterRegexpResponder, RegisterResponderWithQuery and RegisterNoResponder.

View Source
var ErrSubmatchNotFound = errors.New("submatch not found")

ErrSubmatchNotFound is the error returned by GetSubmatch* functions when the given submatch index cannot be found.

View Source
var InitialTransport = http.DefaultTransport

InitialTransport is a cache of the original transport used so we can put it back when Deactivate is called.

View Source
var NoResponderFound = internal.NoResponderFound

NoResponderFound is returned when no responders are found for a given HTTP method and URL.

Functions

func Activate

func Activate()

Activate starts the mock environment. This should be called before your tests run. Under the hood this replaces the http.Client.Transport field of http.DefaultClient with DefaultTransport.

To enable mocks for a test, simply activate at the beginning of a test:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  // all http requests using http.DefaultTransport will now be intercepted
}

If you want all of your tests in a package to be mocked, just call Activate from init():

func init() {
  httpmock.Activate()
}

or using a TestMain function:

func TestMain(m *testing.M) {
  httpmock.Activate()
  os.Exit(m.Run())
}

func ActivateNonDefault

func ActivateNonDefault(client *http.Client)

ActivateNonDefault starts the mock environment with a non-default *http.Client. This emulates the Activate function, but allows for custom clients that do not use http.DefaultTransport.

To enable mocks for a test using a custom client, activate at the beginning of a test:

client := &http.Client{Transport: &http.Transport{TLSHandshakeTimeout: 60 * time.Second}}
httpmock.ActivateNonDefault(client)

func ConnectionFailure

func ConnectionFailure(*http.Request) (*http.Response, error)

ConnectionFailure is a responder that returns a connection failure. This is the default responder and is called when no other matching responder is found. See RegisterNoResponder to override this default behavior.

func Deactivate

func Deactivate()

Deactivate shuts down the mock environment. Any HTTP calls made after this will use a live transport.

Usually you'll call it in a defer right after activating the mock environment:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.Deactivate()

  // when this test ends, the mock environment will close
}

Since go 1.14 you can also use *testing.T.Cleanup method as in:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  t.Cleanup(httpmock.Deactivate)

  // when this test ends, the mock environment will close
}

useful in test helpers to save your callers from calling defer themselves.

func DeactivateAndReset

func DeactivateAndReset()

DeactivateAndReset is just a convenience method for calling Deactivate and then Reset.

Happy deferring!

func Disabled

func Disabled() bool

Disabled allows to test whether httpmock is enabled or not. It depends on GONOMOCKS environment variable.

func GetCallCountInfo

func GetCallCountInfo() map[string]int

GetCallCountInfo gets the info on all the calls httpmock has caught since it was activated or reset. The info is returned as a map of the calling keys with the number of calls made to them as their value. The key is the method, a space, and the URL all concatenated together.

As a special case, regexp responders generate 2 entries for each call. One for the call caught and the other for the rule that matched. For example:

RegisterResponder("GET", `=~z\.com\z`, NewStringResponder(200, "body"))
http.Get("http://z.com")

will generate the following result:

map[string]int{
  `GET http://z.com`: 1,
  `GET =~z\.com\z`:   1,
}

func GetSubmatch added in v1.0.4

func GetSubmatch(req *http.Request, n int) (string, error)

GetSubmatch has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as a string. Example:

RegisterResponder("GET", `=~^/item/name/([^/]+)\z`,
  func(req *http.Request) (*http.Response, error) {
    name, err := GetSubmatch(req, 1) // 1=first regexp submatch
    if err != nil {
      return nil, err
    }
    return NewJsonResponse(200, map[string]any{
      "id":   123,
      "name": name,
    })
  })

It panics if n < 1. See MustGetSubmatch to avoid testing the returned error.

func GetSubmatchAsFloat added in v1.0.4

func GetSubmatchAsFloat(req *http.Request, n int) (float64, error)

GetSubmatchAsFloat has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as a float64. Example:

RegisterResponder("PATCH", `=~^/item/id/\d+\?height=(\d+(?:\.\d*)?)\z`,
  func(req *http.Request) (*http.Response, error) {
    height, err := GetSubmatchAsFloat(req, 1) // 1=first regexp submatch
    if err != nil {
      return nil, err
    }
    return NewJsonResponse(200, map[string]any{
      "id":     id,
      "name":   "The beautiful name",
      "height": height,
    })
  })

It panics if n < 1. See MustGetSubmatchAsFloat to avoid testing the returned error.

func GetSubmatchAsInt added in v1.0.4

func GetSubmatchAsInt(req *http.Request, n int) (int64, error)

GetSubmatchAsInt has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as an int64. Example:

RegisterResponder("GET", `=~^/item/id/(\d+)\z`,
  func(req *http.Request) (*http.Response, error) {
    id, err := GetSubmatchAsInt(req, 1) // 1=first regexp submatch
    if err != nil {
      return nil, err
    }
    return NewJsonResponse(200, map[string]any{
      "id":   id,
      "name": "The beautiful name",
    })
  })

It panics if n < 1. See MustGetSubmatchAsInt to avoid testing the returned error.

func GetSubmatchAsUint added in v1.0.4

func GetSubmatchAsUint(req *http.Request, n int) (uint64, error)

GetSubmatchAsUint has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as a uint64. Example:

RegisterResponder("GET", `=~^/item/id/(\d+)\z`,
  func(req *http.Request) (*http.Response, error) {
    id, err := GetSubmatchAsUint(req, 1) // 1=first regexp submatch
    if err != nil {
      return nil, err
    }
    return NewJsonResponse(200, map[string]any{
      "id":   id,
      "name": "The beautiful name",
    })
  })

It panics if n < 1. See MustGetSubmatchAsUint to avoid testing the returned error.

func GetTotalCallCount

func GetTotalCallCount() int

GetTotalCallCount gets the total number of calls httpmock has taken since it was activated or reset.

func IgnoreMatcherHelper added in v1.3.0

func IgnoreMatcherHelper(skip ...int)

IgnoreMatcherHelper should be called by external helpers building Matcher, typically in an init() function, to avoid they appear in the autogenerated Matcher names.

func MustGetSubmatch added in v1.0.4

func MustGetSubmatch(req *http.Request, n int) string

MustGetSubmatch works as GetSubmatch except that it panics in case of error (submatch not found). It has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as a string. Example:

RegisterResponder("GET", `=~^/item/name/([^/]+)\z`,
  func(req *http.Request) (*http.Response, error) {
    name := MustGetSubmatch(req, 1) // 1=first regexp submatch
    return NewJsonResponse(200, map[string]any{
      "id":   123,
      "name": name,
    })
  })

It panics if n < 1.

func MustGetSubmatchAsFloat added in v1.0.4

func MustGetSubmatchAsFloat(req *http.Request, n int) float64

MustGetSubmatchAsFloat works as GetSubmatchAsFloat except that it panics in case of error (submatch not found or invalid float64 format). It has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as a float64. Example:

RegisterResponder("PATCH", `=~^/item/id/\d+\?height=(\d+(?:\.\d*)?)\z`,
  func(req *http.Request) (*http.Response, error) {
    height := MustGetSubmatchAsFloat(req, 1) // 1=first regexp submatch
    return NewJsonResponse(200, map[string]any{
      "id":     id,
      "name":   "The beautiful name",
      "height": height,
    })
  })

It panics if n < 1.

func MustGetSubmatchAsInt added in v1.0.4

func MustGetSubmatchAsInt(req *http.Request, n int) int64

MustGetSubmatchAsInt works as GetSubmatchAsInt except that it panics in case of error (submatch not found or invalid int64 format). It has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as an int64. Example:

RegisterResponder("GET", `=~^/item/id/(\d+)\z`,
  func(req *http.Request) (*http.Response, error) {
    id := MustGetSubmatchAsInt(req, 1) // 1=first regexp submatch
    return NewJsonResponse(200, map[string]any{
      "id":   id,
      "name": "The beautiful name",
    })
  })

It panics if n < 1.

func MustGetSubmatchAsUint added in v1.0.4

func MustGetSubmatchAsUint(req *http.Request, n int) uint64

MustGetSubmatchAsUint works as GetSubmatchAsUint except that it panics in case of error (submatch not found or invalid uint64 format). It has to be used in Responders installed by RegisterRegexpResponder or RegisterResponder + "=~" URL prefix (as well as MockTransport.RegisterRegexpResponder or MockTransport.RegisterResponder). It allows to retrieve the n-th submatch of the matching regexp, as a uint64. Example:

RegisterResponder("GET", `=~^/item/id/(\d+)\z`,
  func(req *http.Request) (*http.Response, error) {
    id, err := MustGetSubmatchAsUint(req, 1) // 1=first regexp submatch
    return NewJsonResponse(200, map[string]any{
      "id":   id,
      "name": "The beautiful name",
    })
  })

It panics if n < 1.

func NewBytesResponse

func NewBytesResponse(status int, body []byte) *http.Response

NewBytesResponse creates an *http.Response with a body based on the given bytes. Also accepts an HTTP status code.

To pass the content of an existing file as body use File as in:

httpmock.NewBytesResponse(200, httpmock.File("body.raw").Bytes())

func NewJsonResponse

func NewJsonResponse(status int, body any) (*http.Response, error)

NewJsonResponse creates an *http.Response with a body that is a JSON encoded representation of the given any. Also accepts an HTTP status code.

To pass the content of an existing file as body use File as in:

httpmock.NewJsonResponse(200, httpmock.File("body.json"))

func NewRespBodyFromBytes

func NewRespBodyFromBytes(body []byte) io.ReadCloser

NewRespBodyFromBytes creates an io.ReadCloser from a byte slice that is suitable for use as an HTTP response body.

To pass the content of an existing file as body use File as in:

httpmock.NewRespBodyFromBytes(httpmock.File("body.txt").Bytes())

func NewRespBodyFromString

func NewRespBodyFromString(body string) io.ReadCloser

NewRespBodyFromString creates an io.ReadCloser from a string that is suitable for use as an HTTP response body.

To pass the content of an existing file as body use File as in:

httpmock.NewRespBodyFromString(httpmock.File("body.txt").String())

func NewStringResponse

func NewStringResponse(status int, body string) *http.Response

NewStringResponse creates an *http.Response with a body based on the given string. Also accepts an HTTP status code.

To pass the content of an existing file as body use File as in:

httpmock.NewStringResponse(200, httpmock.File("body.txt").String())

func NewXmlResponse

func NewXmlResponse(status int, body any) (*http.Response, error)

NewXmlResponse creates an *http.Response with a body that is an XML encoded representation of the given any. Also accepts an HTTP status code.

To pass the content of an existing file as body use File as in:

httpmock.NewXmlResponse(200, httpmock.File("body.xml"))

func RegisterMatcherResponder added in v1.3.0

func RegisterMatcherResponder(method, url string, matcher Matcher, responder Responder)

RegisterMatcherResponder adds a new responder, associated with a given HTTP method, URL (or path) and Matcher.

When a request comes in that matches, the responder is called and the response returned to the client.

If url contains query parameters, their order matters as well as their content. All following URLs are here considered as different:

http://z.tld?a=1&b=1
http://z.tld?b=1&a=1
http://z.tld?a&b
http://z.tld?a=&b=

If url begins with "=~", the following chars are considered as a regular expression. If this regexp can not be compiled, it panics. Note that the "=~" prefix remains in statistics returned by GetCallCountInfo. As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method & same regexp string) replaces its responder, but does not change its position.

Registering an already existing responder resets the corresponding statistics as returned by GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by GetCallCountInfo. It does nothing if it does not already exist. The original matcher can be passed but also a new Matcher with the same name and a nil match function as in:

NewMatcher("original matcher name", nil)

See RegisterRegexpMatcherResponder to directly pass a *regexp.Regexp.

Example:

func TestCreateArticle(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  // Mock POST /item only if `"name":"Bob"` is found in request body
  httpmock.RegisterMatcherResponder("POST", "/item",
    httpmock.BodyContainsString(`"name":"Bob"`),
    httpmock.NewStringResponder(201, `{"id":1234}`))

  // Can be more acurate with github.com/maxatome/tdhttpmock package
	// paired with github.com/maxatome/go-testdeep/td operators as in
  httpmock.RegisterMatcherResponder("POST", "/item",
	  tdhttpmock.JSONBody(td.JSONPointer("/name", "Alice")),
    httpmock.NewStringResponder(201, `{"id":4567}`))

  // POST requests to http://anything/item with body containing either
  // `"name":"Bob"` or a JSON message with key "name" set to "Alice"
  // value return the corresponding "id" response
}

If several responders are registered for a same method and url couple, but with different matchers, they are ordered depending on the following rules:

  • the zero matcher, Matcher{} (or responder set using RegisterResponder) is always called lastly;
  • other matchers are ordered by their name. If a matcher does not have an explicit name (NewMatcher called with an empty name and Matcher.WithName method not called), a name is automatically computed so all anonymous matchers are sorted by their creation order. An automatically computed name has always the form "~HEXANUMBER@PKG.FUNC() FILE:LINE". See NewMatcher for details.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See also RegisterResponder if a matcher is not needed.

Note that github.com/maxatome/tdhttpmock provides powerful helpers to create matchers with the help of github.com/maxatome/go-testdeep.

func RegisterMatcherResponderWithQuery added in v1.3.0

func RegisterMatcherResponderWithQuery(method, path string, query any, matcher Matcher, responder Responder)

RegisterMatcherResponderWithQuery is same as RegisterMatcherResponder, but it doesn't depend on query items order.

If query is non-nil, its type can be:

If the query type is not recognized or the string cannot be parsed using url.ParseQuery, a panic() occurs.

Unlike RegisterMatcherResponder, path cannot be prefixed by "=~" to say it is a regexp. If it is, a panic occurs.

Registering an already existing responder resets the corresponding statistics as returned by GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by GetCallCountInfo. It does nothing if it does not already exist. The original matcher can be passed but also a new Matcher with the same name and a nil match function as in:

NewMatcher("original matcher name", nil)

If several responders are registered for a same method, path and query tuple, but with different matchers, they are ordered depending on the following rules:

  • the zero matcher, Matcher{} (or responder set using .RegisterResponderWithQuery) is always called lastly;
  • other matchers are ordered by their name. If a matcher does not have an explicit name (NewMatcher called with an empty name and Matcher.WithName method not called), a name is automatically computed so all anonymous matchers are sorted by their creation order. An automatically computed name has always the form "~HEXANUMBER@PKG.FUNC() FILE:LINE". See NewMatcher for details.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See also RegisterResponderWithQuery if a matcher is not needed.

Note that github.com/maxatome/tdhttpmock provides powerful helpers to create matchers with the help of github.com/maxatome/go-testdeep.

func RegisterNoResponder

func RegisterNoResponder(responder Responder)

RegisterNoResponder is used to register a responder that is called if no other responders are found. The default is ConnectionFailure that returns a connection error.

Use it in conjunction with NewNotFoundResponder to ensure that all routes have been mocked:

import (
  "testing"
  "github.com/jarcoal/httpmock"
)
...
func TestMyApp(t *testing.T) {
   ...
   // Calls testing.Fatal with the name of Responder-less route and
   // the stack trace of the call.
   httpmock.RegisterNoResponder(httpmock.NewNotFoundResponder(t.Fatal))

will abort the current test and print something like:

transport_test.go:735: Called from net/http.Get()
      at /go/src/github.com/jarcoal/httpmock/transport_test.go:714
    github.com/jarcoal/httpmock.TestCheckStackTracer()
      at /go/src/testing/testing.go:865
    testing.tRunner()
      at /go/src/runtime/asm_amd64.s:1337

If responder is passed as nil, the default behavior (ConnectionFailure) is re-enabled.

In some cases you may not want all URLs to be mocked, in which case you can do this:

func TestFetchArticles(t *testing.T) {
  ...
  httpmock.RegisterNoResponder(httpmock.InitialTransport.RoundTrip)

  // any requests that don't have a registered URL will be fetched normally
}

func RegisterRegexpMatcherResponder added in v1.3.0

func RegisterRegexpMatcherResponder(method string, urlRegexp *regexp.Regexp, matcher Matcher, responder Responder)

RegisterRegexpMatcherResponder adds a new responder, associated with a given HTTP method, URL (or path) regular expression and Matcher.

When a request comes in that matches, the responder is called and the response returned to the client.

As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method, same regexp string and same Matcher name) replaces its responder, but does not change its position, and resets the corresponding statistics as returned by GetCallCountInfo.

If several responders are registered for a same method and urlRegexp couple, but with different matchers, they are ordered depending on the following rules:

  • the zero matcher, Matcher{} (or responder set using RegisterRegexpResponder) is always called lastly;
  • other matchers are ordered by their name. If a matcher does not have an explicit name (NewMatcher called with an empty name and Matcher.WithName method not called), a name is automatically computed so all anonymous matchers are sorted by their creation order. An automatically computed name has always the form "~HEXANUMBER@PKG.FUNC() FILE:LINE". See NewMatcher for details.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by GetCallCountInfo. It does nothing if it does not already exist. The original matcher can be passed but also a new Matcher with the same name and a nil match function as in:

NewMatcher("original matcher name", nil)

A "=~" prefix is added to the stringified regexp in the statistics returned by GetCallCountInfo.

See RegisterMatcherResponder function and the "=~" prefix in its url parameter to avoid compiling the regexp by yourself.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See RegisterRegexpResponder if a matcher is not needed.

Note that github.com/maxatome/tdhttpmock provides powerful helpers to create matchers with the help of github.com/maxatome/go-testdeep.

func RegisterRegexpResponder added in v1.0.4

func RegisterRegexpResponder(method string, urlRegexp *regexp.Regexp, responder Responder)

RegisterRegexpResponder adds a new responder, associated with a given HTTP method and URL (or path) regular expression.

When a request comes in that matches, the responder is called and the response returned to the client.

As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method & same regexp string) replaces its responder, but does not change its position, and resets the corresponding statistics as returned by GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by GetCallCountInfo. It does nothing if it does not already exist.

A "=~" prefix is added to the stringified regexp in the statistics returned by GetCallCountInfo.

See RegisterResponder function and the "=~" prefix in its url parameter to avoid compiling the regexp by yourself.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting DefaultTransport.DontCheckMethod to true prior to this call.

func RegisterResponder

func RegisterResponder(method, url string, responder Responder)

RegisterResponder adds a new responder, associated with a given HTTP method and URL (or path).

When a request comes in that matches, the responder is called and the response returned to the client.

If url contains query parameters, their order matters as well as their content. All following URLs are here considered as different:

http://z.tld?a=1&b=1
http://z.tld?b=1&a=1
http://z.tld?a&b
http://z.tld?a=&b=

If url begins with "=~", the following chars are considered as a regular expression. If this regexp can not be compiled, it panics. Note that the "=~" prefix remains in statistics returned by GetCallCountInfo. As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method & same regexp string) replaces its responder, but does not change its position.

Registering an already existing responder resets the corresponding statistics as returned by GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by GetCallCountInfo. It does nothing if it does not already exist.

See RegisterRegexpResponder to directly pass a *regexp.Regexp.

Example:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  httpmock.RegisterResponder("GET", "http://example.com/",
    httpmock.NewStringResponder(200, "hello world"))

  httpmock.RegisterResponder("GET", "/path/only",
    httpmock.NewStringResponder(200, "any host hello world"))

  httpmock.RegisterResponder("GET", `=~^/item/id/\d+\z`,
    httpmock.NewStringResponder(200, "any item get"))

  // requests to http://example.com/ now return "hello world" and
  // requests to any host with path /path/only return "any host hello world"
  // requests to any host with path matching ^/item/id/\d+\z regular expression return "any item get"
}

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting DefaultTransport.DontCheckMethod to true prior to this call.

func RegisterResponderWithQuery

func RegisterResponderWithQuery(method, path string, query any, responder Responder)

RegisterResponderWithQuery it is same as RegisterResponder, but doesn't depends on query items order.

query type can be:

If the query type is not recognized or the string cannot be parsed using url.ParseQuery, a panic() occurs.

Unlike RegisterResponder, path cannot be prefixed by "=~" to say it is a regexp. If it is, a panic occurs.

Registering an already existing responder resets the corresponding statistics as returned by GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by GetCallCountInfo. It does nothing if it does not already exist.

Example using a url.Values:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  expectedQuery := net.Values{
    "a": []string{"3", "1", "8"},
    "b": []string{"4", "2"},
  }
  httpmock.RegisterResponderWithQueryValues(
    "GET", "http://example.com/", expectedQuery,
    httpmock.NewStringResponder(200, "hello world"))

  // requests to http://example.com?a=1&a=3&a=8&b=2&b=4
  //      and to http://example.com?b=4&a=2&b=2&a=8&a=1
  // now return 'hello world'
}

or using a map[string]string:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  expectedQuery := map[string]string{
    "a": "1",
    "b": "2"
  }
  httpmock.RegisterResponderWithQuery(
    "GET", "http://example.com/", expectedQuery,
    httpmock.NewStringResponder(200, "hello world"))

  // requests to http://example.com?a=1&b=2 and http://example.com?b=2&a=1 now return 'hello world'
}

or using a query string:

func TestFetchArticles(t *testing.T) {
  httpmock.Activate()
  defer httpmock.DeactivateAndReset()

  expectedQuery := "a=3&b=4&b=2&a=1&a=8"
  httpmock.RegisterResponderWithQueryValues(
    "GET", "http://example.com/", expectedQuery,
    httpmock.NewStringResponder(200, "hello world"))

  // requests to http://example.com?a=1&a=3&a=8&b=2&b=4
  //      and to http://example.com?b=4&a=2&b=2&a=8&a=1
  // now return 'hello world'
}

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting DefaultTransport.DontCheckMethod to true prior to this call.

func Reset

func Reset()

Reset removes any registered mocks and returns the mock environment to its initial state. It zeroes call counters too.

func ZeroCallCounters added in v1.0.5

func ZeroCallCounters()

ZeroCallCounters zeroes call counters without touching registered responders.

Types

type File added in v1.0.7

type File string

File is a file name. The contents of this file is loaded on demand by the following methods.

Note that:

file := httpmock.File("file.txt")
fmt.Printf("file: %s\n", file)

prints the content of file "file.txt" as File.String method is used.

To print the file name, and not its content, simply do:

file := httpmock.File("file.txt")
fmt.Printf("file: %s\n", string(file))

func (File) Bytes added in v1.0.7

func (f File) Bytes() []byte

Bytes returns the content of file as a []byte. If an error occurs during the opening or reading of the file, it panics.

Useful to be used in conjunction with NewBytesResponse or NewBytesResponder as in:

httpmock.NewBytesResponder(200, httpmock.File("body.raw").Bytes())

func (File) MarshalJSON added in v1.0.7

func (f File) MarshalJSON() ([]byte, error)

MarshalJSON implements encoding/json.Marshaler.

Useful to be used in conjunction with NewJsonResponse or NewJsonResponder as in:

httpmock.NewJsonResponder(200, httpmock.File("body.json"))

func (File) String added in v1.0.7

func (f File) String() string

String implements fmt.Stringer and returns the content of file as a string. If an error occurs during the opening or reading of the file, it panics.

Useful to be used in conjunction with NewStringResponse or NewStringResponder as in:

httpmock.NewStringResponder(200, httpmock.File("body.txt").String())

type Matcher added in v1.3.0

type Matcher struct {
	// contains filtered or unexported fields
}

Matcher type defines a match case. The zero Matcher{} corresponds to the default case. Otherwise, use NewMatcher or any helper building a Matcher like BodyContainsBytes, BodyContainsBytes, HeaderExists, HeaderIs, HeaderContains or any of github.com/maxatome/tdhttpmock functions.

func BodyContainsBytes added in v1.3.0

func BodyContainsBytes(subslice []byte) Matcher

BodyContainsBytes returns a Matcher checking that request body contains subslice.

The name of the returned Matcher is auto-generated (see NewMatcher). To name it explicitly, use Matcher.WithName as in:

BodyContainsBytes([]byte("foo")).WithName("10-body-contains-foo")

See also github.com/maxatome/tdhttpmock.Body, github.com/maxatome/tdhttpmock.JSONBody and github.com/maxatome/tdhttpmock.XMLBody for powerful body testing.

func BodyContainsString added in v1.3.0

func BodyContainsString(substr string) Matcher

BodyContainsString returns a Matcher checking that request body contains substr.

The name of the returned Matcher is auto-generated (see NewMatcher). To name it explicitly, use Matcher.WithName as in:

BodyContainsString("foo").WithName("10-body-contains-foo")

See also github.com/maxatome/tdhttpmock.Body, github.com/maxatome/tdhttpmock.JSONBody and github.com/maxatome/tdhttpmock.XMLBody for powerful body testing.

func HeaderContains added in v1.3.0

func HeaderContains(key, substr string) Matcher

HeaderContains returns a Matcher checking that request contains key header itself containing substr.

The name of the returned Matcher is auto-generated (see NewMatcher). To name it explicitly, use Matcher.WithName as in:

HeaderContains("X-Custom", "VALUE").WithName("10-custom-contains-value")

See also github.com/maxatome/tdhttpmock.Header for powerful header testing.

func HeaderExists added in v1.3.0

func HeaderExists(key string) Matcher

HeaderExists returns a Matcher checking that request contains key header.

The name of the returned Matcher is auto-generated (see NewMatcher). To name it explicitly, use Matcher.WithName as in:

HeaderExists("X-Custom").WithName("10-custom-exists")

See also github.com/maxatome/tdhttpmock.Header for powerful header testing.

func HeaderIs added in v1.3.0

func HeaderIs(key, value string) Matcher

HeaderIs returns a Matcher checking that request contains key header set to value.

The name of the returned Matcher is auto-generated (see NewMatcher). To name it explicitly, use Matcher.WithName as in:

HeaderIs("X-Custom", "VALUE").WithName("10-custom-is-value")

See also github.com/maxatome/tdhttpmock.Header for powerful header testing.

func NewMatcher added in v1.3.0

func NewMatcher(name string, fn MatcherFunc) Matcher

NewMatcher returns a Matcher. If name is empty and fn is non-nil, a name is automatically generated. When fn is nil, it is a default Matcher: its name can be empty.

Automatically generated names have the form:

~HEXANUMBER@PKG.FUNC() FILE:LINE

Legend:

  • HEXANUMBER is a unique 10 digit hexadecimal number, always increasing;
  • PKG is the NewMatcher caller package (except if IgnoreMatcherHelper has been previously called, in this case it is the caller of the caller package and so on);
  • FUNC is the function name of the caller in the previous PKG package;
  • FILE and LINE are the location of the call in FUNC function.

func (Matcher) And added in v1.3.0

func (m Matcher) And(ms ...Matcher) Matcher

And combines m and all ms in a new Matcher. This new Matcher succeeds if all of m and ms succeed. Note that a Matcher also succeeds if Matcher MatcherFunc is nil. The name of returned Matcher is m's one if the empty/default Matcher is returned.

func (Matcher) Check added in v1.3.0

func (m Matcher) Check(req *http.Request) bool

Check returns true if req is matched by m.

func (Matcher) Name added in v1.3.0

func (m Matcher) Name() string

Name returns the m's name.

func (Matcher) Or added in v1.3.0

func (m Matcher) Or(ms ...Matcher) Matcher

Or combines m and all ms in a new Matcher. This new Matcher succeeds if one of m or ms succeeds. Note that as a Matcher succeeds if internal fn is nil, if m's internal fn or any of ms item's internal fn is nil, the returned Matcher always succeeds. The name of returned Matcher is m's one.

func (Matcher) WithName added in v1.3.0

func (m Matcher) WithName(name string) Matcher

WithName returns a new Matcher based on m with name name.

type MatcherFunc added in v1.3.0

type MatcherFunc func(req *http.Request) bool

MatcherFunc type is the function to use to check a Matcher matches an incoming request. When httpmock calls a function of this type, it is guaranteed req.Body is never nil. If req.Body is nil in the original request, it is temporarily replaced by an instance returning always io.EOF for each Read() call, during the call.

func (MatcherFunc) And added in v1.3.0

func (mf MatcherFunc) And(mfs ...MatcherFunc) MatcherFunc

And combines mf and all mfs in a new MatcherFunc. This new MatcherFunc succeeds if all of mf and mfs succeed. Note that a MatcherFunc also succeeds if it is nil, so if mf and all mfs items are nil, nil is returned.

func (MatcherFunc) Check added in v1.3.0

func (mf MatcherFunc) Check(req *http.Request) bool

Check returns true if mf is nil, otherwise it returns mf(req).

func (MatcherFunc) Or added in v1.3.0

func (mf MatcherFunc) Or(mfs ...MatcherFunc) MatcherFunc

Or combines mf and all mfs in a new MatcherFunc. This new MatcherFunc succeeds if one of mf or mfs succeeds. Note that as a a nil MatcherFunc is considered succeeding, if mf or one of mfs items is nil, nil is returned.

type MockTransport

type MockTransport struct {
	// DontCheckMethod disables standard methods check. By default, if
	// a responder is registered using a lower-cased method among CONNECT,
	// DELETE, GET, HEAD, OPTIONS, POST, PUT and TRACE, a panic occurs
	// as it is probably a mistake.
	DontCheckMethod bool
	// contains filtered or unexported fields
}

MockTransport implements http.RoundTripper interface, which fulfills single HTTP requests issued by an http.Client. This implementation doesn't actually make the call, instead deferring to the registered list of responders.

func NewMockTransport

func NewMockTransport() *MockTransport

NewMockTransport creates a new *MockTransport with no responders.

func (*MockTransport) GetCallCountInfo

func (m *MockTransport) GetCallCountInfo() map[string]int

GetCallCountInfo gets the info on all the calls m has caught since it was activated or reset. The info is returned as a map of the calling keys with the number of calls made to them as their value. The key is the method, a space, and the URL all concatenated together.

As a special case, regexp responders generate 2 entries for each call. One for the call caught and the other for the rule that matched. For example:

RegisterResponder("GET", `=~z\.com\z`, NewStringResponder(200, "body"))
http.Get("http://z.com")

will generate the following result:

map[string]int{
  `GET http://z.com`: 1,
  `GET =~z\.com\z`:   1,
}

func (*MockTransport) GetTotalCallCount

func (m *MockTransport) GetTotalCallCount() int

GetTotalCallCount gets the total number of calls m has taken since it was activated or reset.

func (*MockTransport) NumResponders added in v1.0.7

func (m *MockTransport) NumResponders() int

NumResponders returns the number of responders currently in use. The responder registered with MockTransport.RegisterNoResponder is not taken into account.

func (*MockTransport) RegisterMatcherResponder added in v1.3.0

func (m *MockTransport) RegisterMatcherResponder(method, url string, matcher Matcher, responder Responder)

RegisterMatcherResponder adds a new responder, associated with a given HTTP method, URL (or path) and Matcher.

When a request comes in that matches, the responder is called and the response returned to the client.

If url contains query parameters, their order matters as well as their content. All following URLs are here considered as different:

http://z.tld?a=1&b=1
http://z.tld?b=1&a=1
http://z.tld?a&b
http://z.tld?a=&b=

If url begins with "=~", the following chars are considered as a regular expression. If this regexp can not be compiled, it panics. Note that the "=~" prefix remains in statistics returned by MockTransport.GetCallCountInfo. As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method & same regexp string) replaces its responder, but does not change its position.

Registering an already existing responder resets the corresponding statistics as returned by MockTransport.GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by MockTransport.GetCallCountInfo. It does nothing if it does not already exist. The original matcher can be passed but also a new Matcher with the same name and a nil match function as in:

NewMatcher("original matcher name", nil)

See MockTransport.RegisterRegexpMatcherResponder to directly pass a *regexp.Regexp.

If several responders are registered for a same method and url couple, but with different matchers, they are ordered depending on the following rules:

  • the zero matcher, Matcher{} (or responder set using MockTransport.RegisterResponder) is always called lastly;
  • other matchers are ordered by their name. If a matcher does not have an explicit name (NewMatcher called with an empty name and Matcher.WithName method not called), a name is automatically computed so all anonymous matchers are sorted by their creation order. An automatically computed name has always the form "~HEXANUMBER@PKG.FUNC() FILE:LINE". See NewMatcher for details.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See also MockTransport.RegisterResponder if a matcher is not needed.

Note that github.com/maxatome/tdhttpmock provides powerful helpers to create matchers with the help of github.com/maxatome/go-testdeep.

func (*MockTransport) RegisterMatcherResponderWithQuery added in v1.3.0

func (m *MockTransport) RegisterMatcherResponderWithQuery(method, path string, query any, matcher Matcher, responder Responder)

RegisterMatcherResponderWithQuery is same as MockTransport.RegisterMatcherResponder, but it doesn't depend on query items order.

If query is non-nil, its type can be:

If the query type is not recognized or the string cannot be parsed using url.ParseQuery, a panic() occurs.

Unlike MockTransport.RegisterMatcherResponder, path cannot be prefixed by "=~" to say it is a regexp. If it is, a panic occurs.

Registering an already existing responder resets the corresponding statistics as returned by MockTransport.GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by MockTransport.GetCallCountInfo. It does nothing if it does not already exist. The original matcher can be passed but also a new Matcher with the same name and a nil match function as in:

NewMatcher("original matcher name", nil)

If several responders are registered for a same method, path and query tuple, but with different matchers, they are ordered depending on the following rules:

  • the zero matcher, Matcher{} (or responder set using MockTransport.RegisterResponderWithQuery) is always called lastly;
  • other matchers are ordered by their name. If a matcher does not have an explicit name (NewMatcher called with an empty name and Matcher.WithName method not called), a name is automatically computed so all anonymous matchers are sorted by their creation order. An automatically computed name has always the form "~HEXANUMBER@PKG.FUNC() FILE:LINE". See NewMatcher for details.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See also MockTransport.RegisterResponderWithQuery if a matcher is not needed.

Note that github.com/maxatome/tdhttpmock provides powerful helpers to create matchers with the help of github.com/maxatome/go-testdeep.

func (*MockTransport) RegisterNoResponder

func (m *MockTransport) RegisterNoResponder(responder Responder)

RegisterNoResponder is used to register a responder that is called if no other responders are found. The default is ConnectionFailure that returns a connection error.

Use it in conjunction with NewNotFoundResponder to ensure that all routes have been mocked:

func TestMyApp(t *testing.T) {
   ...
   // Calls testing.Fatal with the name of Responder-less route and
   // the stack trace of the call.
   mock.RegisterNoResponder(httpmock.NewNotFoundResponder(t.Fatal))

will abort the current test and print something like:

transport_test.go:735: Called from net/http.Get()
      at /go/src/github.com/jarcoal/httpmock/transport_test.go:714
    github.com/jarcoal/httpmock.TestCheckStackTracer()
      at /go/src/testing/testing.go:865
    testing.tRunner()
      at /go/src/runtime/asm_amd64.s:1337

If responder is passed as nil, the default behavior (ConnectionFailure) is re-enabled.

In some cases you may not want all URLs to be mocked, in which case you can do this:

func TestFetchArticles(t *testing.T) {
  ...
  mock.RegisterNoResponder(httpmock.InitialTransport.RoundTrip)

  // any requests that don't have a registered URL will be fetched normally
}

func (*MockTransport) RegisterRegexpMatcherResponder added in v1.3.0

func (m *MockTransport) RegisterRegexpMatcherResponder(method string, urlRegexp *regexp.Regexp, matcher Matcher, responder Responder)

RegisterRegexpMatcherResponder adds a new responder, associated with a given HTTP method, URL (or path) regular expression and Matcher.

When a request comes in that matches, the responder is called and the response returned to the client.

As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method, same regexp string and same Matcher name) replaces its responder, but does not change its position, and resets the corresponding statistics as returned by MockTransport.GetCallCountInfo.

If several responders are registered for a same method and urlRegexp couple, but with different matchers, they are ordered depending on the following rules:

  • the zero matcher, Matcher{} (or responder set using MockTransport.RegisterRegexpResponder) is always called lastly;
  • other matchers are ordered by their name. If a matcher does not have an explicit name (NewMatcher called with an empty name and Matcher.WithName method not called), a name is automatically computed so all anonymous matchers are sorted by their creation order. An automatically computed name has always the form "~HEXANUMBER@PKG.FUNC() FILE:LINE". See NewMatcher for details.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by MockTransport.GetCallCountInfo. It does nothing if it does not already exist. The original matcher can be passed but also a new Matcher with the same name and a nil match function as in:

NewMatcher("original matcher name", nil)

A "=~" prefix is added to the stringified regexp in the statistics returned by MockTransport.GetCallCountInfo.

See MockTransport.RegisterMatcherResponder function and the "=~" prefix in its url parameter to avoid compiling the regexp by yourself.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See MockTransport.RegisterRegexpResponder if a matcher is not needed.

Note that github.com/maxatome/tdhttpmock provides powerful helpers to create matchers with the help of github.com/maxatome/go-testdeep.

func (*MockTransport) RegisterRegexpResponder added in v1.0.4

func (m *MockTransport) RegisterRegexpResponder(method string, urlRegexp *regexp.Regexp, responder Responder)

RegisterRegexpResponder adds a new responder, associated with a given HTTP method and URL (or path) regular expression.

When a request comes in that matches, the responder is called and the response returned to the client.

As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method & same regexp string) replaces its responder, but does not change its position, and resets the corresponding statistics as returned by MockTransport.GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by [MockTransport.MockTransportGetCallCountInfo]. It does nothing if it does not already exist.

A "=~" prefix is added to the stringified regexp in the statistics returned by MockTransport.GetCallCountInfo.

See MockTransport.RegisterResponder function and the "=~" prefix in its url parameter to avoid compiling the regexp by yourself.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See MockTransport.RegisterRegexpMatcherResponder to also match on request header and/or body.

func (*MockTransport) RegisterResponder

func (m *MockTransport) RegisterResponder(method, url string, responder Responder)

RegisterResponder adds a new responder, associated with a given HTTP method and URL (or path).

When a request comes in that matches, the responder is called and the response returned to the client.

If url contains query parameters, their order matters as well as their content. All following URLs are here considered as different:

http://z.tld?a=1&b=1
http://z.tld?b=1&a=1
http://z.tld?a&b
http://z.tld?a=&b=

If url begins with "=~", the following chars are considered as a regular expression. If this regexp can not be compiled, it panics. Note that the "=~" prefix remains in statistics returned by MockTransport.GetCallCountInfo. As 2 regexps can match the same URL, the regexp responders are tested in the order they are registered. Registering an already existing regexp responder (same method & same regexp string) replaces its responder, but does not change its position.

Registering an already existing responder resets the corresponding statistics as returned by MockTransport.GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by MockTransport.GetCallCountInfo. It does nothing if it does not already exist.

See MockTransport.RegisterRegexpResponder to directly pass a *regexp.Regexp.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See MockTransport.RegisterMatcherResponder to also match on request header and/or body.

func (*MockTransport) RegisterResponderWithQuery

func (m *MockTransport) RegisterResponderWithQuery(method, path string, query any, responder Responder)

RegisterResponderWithQuery is same as MockTransport.RegisterResponder, but it doesn't depend on query items order.

If query is non-nil, its type can be:

If the query type is not recognized or the string cannot be parsed using url.ParseQuery, a panic() occurs.

Unlike MockTransport.RegisterResponder, path cannot be prefixed by "=~" to say it is a regexp. If it is, a panic occurs.

Registering an already existing responder resets the corresponding statistics as returned by MockTransport.GetCallCountInfo.

Registering a nil Responder removes the existing one and the corresponding statistics as returned by MockTransport.GetCallCountInfo. It does nothing if it does not already exist.

If method is a lower-cased version of CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT or TRACE, a panics occurs to notice the possible mistake. This panic can be disabled by setting m.DontCheckMethod to true prior to this call.

See MockTransport.RegisterMatcherResponderWithQuery to also match on request header and/or body.

func (*MockTransport) Reset

func (m *MockTransport) Reset()

Reset removes all registered responders (including the no responder) from the MockTransport. It zeroes call counters too.

func (*MockTransport) Responders added in v1.2.0

func (m *MockTransport) Responders() []string

Responders returns the list of currently registered responders. Each responder is listed as a string containing "METHOD URL". Non-regexp responders are listed first in alphabetical order (sorted by URL then METHOD), then regexp responders in the order they have been registered.

The responder registered with MockTransport.RegisterNoResponder is not listed.

func (*MockTransport) RoundTrip

func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip receives HTTP requests and routes them to the appropriate responder. It is required to implement the http.RoundTripper interface. You will not interact with this directly, instead the *http.Client you are using will call it for you.

func (*MockTransport) ZeroCallCounters added in v1.0.5

func (m *MockTransport) ZeroCallCounters()

ZeroCallCounters zeroes call counters without touching registered responders.

type Responder

type Responder func(*http.Request) (*http.Response, error)

Responder is a callback that receives an *http.Request and returns a mocked response.

func NewBytesResponder

func NewBytesResponder(status int, body []byte) Responder

NewBytesResponder creates a Responder from a given body (as a byte slice) and status code.

To pass the content of an existing file as body use File as in:

httpmock.NewBytesResponder(200, httpmock.File("body.raw").Bytes())

func NewErrorResponder

func NewErrorResponder(err error) Responder

NewErrorResponder creates a Responder that returns an empty request and the given error. This can be used to e.g. imitate more deep http errors for the client.

func NewJsonResponder

func NewJsonResponder(status int, body any) (Responder, error)

NewJsonResponder creates a Responder from a given body (as an any that is encoded to JSON) and status code.

To pass the content of an existing file as body use File as in:

httpmock.NewJsonResponder(200, httpmock.File("body.json"))

func NewJsonResponderOrPanic

func NewJsonResponderOrPanic(status int, body any) Responder

NewJsonResponderOrPanic is like NewJsonResponder but panics in case of error.

It simplifies the call of RegisterResponder, avoiding the use of a temporary variable and an error check, and so can be used as NewStringResponder or NewBytesResponder in such context:

httpmock.RegisterResponder(
  "GET",
  "/test/path",
  httpmock.NewJsonResponderOrPanic(200, &MyBody),
)

To pass the content of an existing file as body use File as in:

httpmock.NewJsonResponderOrPanic(200, httpmock.File("body.json"))

func NewNotFoundResponder added in v1.0.2

func NewNotFoundResponder(fn func(...any)) Responder

NewNotFoundResponder creates a Responder typically used in conjunction with RegisterNoResponder function and testing package, to be proactive when a Responder is not found. fn is called with a unique string parameter containing the name of the missing route and the stack trace to localize the origin of the call. If fn returns (= if it does not panic), the Responder returns an error of the form: "Responder not found for GET http://foo.bar/path". Note that fn can be nil.

It is useful when writing tests to ensure that all routes have been mocked.

Example of use:

import (
  "testing"
  "github.com/jarcoal/httpmock"
)
...
func TestMyApp(t *testing.T) {
   ...
   // Calls testing.Fatal with the name of Responder-less route and
   // the stack trace of the call.
   httpmock.RegisterNoResponder(httpmock.NewNotFoundResponder(t.Fatal))

Will abort the current test and print something like:

transport_test.go:735: Called from net/http.Get()
      at /go/src/github.com/jarcoal/httpmock/transport_test.go:714
    github.com/jarcoal/httpmock.TestCheckStackTracer()
      at /go/src/testing/testing.go:865
    testing.tRunner()
      at /go/src/runtime/asm_amd64.s:1337

func NewStringResponder

func NewStringResponder(status int, body string) Responder

NewStringResponder creates a Responder from a given body (as a string) and status code.

To pass the content of an existing file as body use File as in:

httpmock.NewStringResponder(200, httpmock.File("body.txt").String())

func NewXmlResponder

func NewXmlResponder(status int, body any) (Responder, error)

NewXmlResponder creates a Responder from a given body (as an any that is encoded to XML) and status code.

To pass the content of an existing file as body use File as in:

httpmock.NewXmlResponder(200, httpmock.File("body.xml"))

func NewXmlResponderOrPanic

func NewXmlResponderOrPanic(status int, body any) Responder

NewXmlResponderOrPanic is like NewXmlResponder but panics in case of error.

It simplifies the call of RegisterResponder, avoiding the use of a temporary variable and an error check, and so can be used as NewStringResponder or NewBytesResponder in such context:

httpmock.RegisterResponder(
  "GET",
  "/test/path",
  httpmock.NewXmlResponderOrPanic(200, &MyBody),
)

To pass the content of an existing file as body use File as in:

httpmock.NewXmlResponderOrPanic(200, httpmock.File("body.xml"))

func ResponderFromMultipleResponses added in v1.0.8

func ResponderFromMultipleResponses(responses []*http.Response, fn ...func(...any)) Responder

ResponderFromMultipleResponses wraps an *http.Response list in a Responder.

Each response will be returned in the order of the provided list. If the Responder is called more than the size of the provided list, an error will be thrown.

Be careful, except for responses generated by httpmock (NewStringResponse and NewBytesResponse functions) for which there is no problems, it is the caller responsibility to ensure the response body can be read several times and concurrently if needed, as it is shared among all Responder returned responses.

For home-made responses, NewRespBodyFromString and NewRespBodyFromBytes functions can be used to produce response bodies that can be read several times and concurrently.

If all responses have been returned and fn is passed and non-nil, it acts as the fn parameter of NewNotFoundResponder, allowing to dump the stack trace to localize the origin of the call.

import (
  "github.com/jarcoal/httpmock"
  "testing"
)
...
func TestMyApp(t *testing.T) {
  ...
  // This responder is callable only once, then an error is returned and
  // the stacktrace of the call logged using t.Log()
  httpmock.RegisterResponder("GET", "/foo/bar",
    httpmock.ResponderFromMultipleResponses(
      []*http.Response{
        httpmock.NewStringResponse(200, `{"name":"bar"}`),
        httpmock.NewStringResponse(404, `{"mesg":"Not found"}`),
      },
      t.Log),
  )
}

See also Responder.Then.

func ResponderFromResponse

func ResponderFromResponse(resp *http.Response) Responder

ResponderFromResponse wraps an *http.Response in a Responder.

Be careful, except for responses generated by httpmock (NewStringResponse and NewBytesResponse functions) for which there is no problems, it is the caller responsibility to ensure the response body can be read several times and concurrently if needed, as it is shared among all Responder returned responses.

For home-made responses, NewRespBodyFromString and NewRespBodyFromBytes functions can be used to produce response bodies that can be read several times and concurrently.

func (Responder) Delay added in v1.1.0

func (r Responder) Delay(d time.Duration) Responder

Delay returns a new Responder that calls the original r Responder after a delay of d.

import (
  "testing"
  "time"
  "github.com/jarcoal/httpmock"
)
...
func TestMyApp(t *testing.T) {
  ...
  httpmock.RegisterResponder("GET", "/foo/bar",
    httpmock.NewStringResponder(200, "{}").Delay(100*time.Millisecond),
  )

func (Responder) HeaderAdd added in v1.3.0

func (r Responder) HeaderAdd(h http.Header) Responder

HeaderAdd returns a new Responder based on r that ensures the returned *http.Response includes h header. It adds each h entry to the header. It appends to any existing values associated with each h key. Each key is case insensitive; it is canonicalized by http.CanonicalHeaderKey.

See also Responder.HeaderSet and Responder.SetContentLength.

func (Responder) HeaderSet added in v1.3.0

func (r Responder) HeaderSet(h http.Header) Responder

HeaderSet returns a new Responder based on r that ensures the returned *http.Response includes h header. It sets the header entries associated with each h key. It replaces any existing values associated each h key. Each key is case insensitive; it is canonicalized by http.CanonicalHeaderKey.

See also Responder.HeaderAdd and Responder.SetContentLength.

func (Responder) Once added in v1.0.3

func (r Responder) Once(fn ...func(...any)) Responder

Once returns a new Responder callable once before returning an error. If the Responder is called 2 or more times and fn is passed and non-nil, it acts as the fn parameter of NewNotFoundResponder, allowing to dump the stack trace to localize the origin of the call.

import (
  "testing"
  "github.com/jarcoal/httpmock"
)
...
func TestMyApp(t *testing.T) {
  ...
  // This responder is callable only once, then an error is returned and
  // the stacktrace of the call logged using t.Log()
  httpmock.RegisterResponder("GET", "/foo/bar",
    httpmock.NewStringResponder(200, "{}").Once(t.Log),
  )

func (Responder) SetContentLength added in v1.3.0

func (r Responder) SetContentLength() Responder

SetContentLength returns a new Responder based on r that ensures the returned *http.Response ContentLength field and Content-Length header are set to the right value.

If r returns an *http.Response with a nil Body or equal to http.NoBody, the length is always set to 0.

If r returned response.Body implements:

Len() int

then the length is set to the Body.Len() returned value. All httpmock generated bodies implement this method. Beware that strings.Builder, strings.Reader, bytes.Buffer and bytes.Reader types used with io.NopCloser do not implement Len() anymore.

Otherwise, r returned response.Body is entirely copied into an internal buffer to get its length, then it is closed. The Body of the *http.Response returned by the Responder returned by SetContentLength can then be read again to return its content as usual. But keep in mind that each time this Responder is called, r is called first. So this one has to carefully handle its body: it is highly recommended to use NewRespBodyFromString or NewRespBodyFromBytes to set the body once (as NewStringResponder and NewBytesResponder do behind the scene), or to build the body each time r is called.

The following calls are all correct:

responder = httpmock.NewStringResponder(200, "BODY").SetContentLength()
responder = httpmock.NewBytesResponder(200, []byte("BODY")).SetContentLength()
responder = ResponderFromResponse(&http.Response{
  // build a body once, but httpmock knows how to "rearm" it once read
  Body:          NewRespBodyFromString("BODY"),
  StatusCode:    200,
}).SetContentLength()
responder = httpmock.Responder(func(req *http.Request) (*http.Response, error) {
  // build a new body for each call
  return &http.Response{
    StatusCode: 200,
    Body:       io.NopCloser(strings.NewReader("BODY")),
  }, nil
}).SetContentLength()

But the following is not correct:

responder = httpmock.ResponderFromResponse(&http.Response{
  StatusCode: 200,
  Body:       io.NopCloser(strings.NewReader("BODY")),
}).SetContentLength()

it will only succeed for the first responder call. The following calls will deliver responses with an empty body, as it will already been read by the first call.

func (Responder) Then added in v1.1.0

func (r Responder) Then(next Responder) (x Responder)

Then returns a new Responder that calls r on first invocation, then next on following ones, except when Then is chained, in this case next is called only once:

A := httpmock.NewStringResponder(200, "A")
B := httpmock.NewStringResponder(200, "B")
C := httpmock.NewStringResponder(200, "C")

httpmock.RegisterResponder("GET", "/pipo", A.Then(B).Then(C))

http.Get("http://foo.bar/pipo") // A is called
http.Get("http://foo.bar/pipo") // B is called
http.Get("http://foo.bar/pipo") // C is called
http.Get("http://foo.bar/pipo") // C is called, and so on

A panic occurs if next is the result of another Then call (because allowing it could cause inextricable problems at runtime). Then calls can be chained, but cannot call each other by parameter. Example:

A.Then(B).Then(C) // is OK
A.Then(B.Then(C)) // panics as A.Then() parameter is another Then() call

See also ResponderFromMultipleResponses.

func (Responder) Times added in v1.0.3

func (r Responder) Times(n int, fn ...func(...any)) Responder

Times returns a Responder callable n times before returning an error. If the Responder is called more than n times and fn is passed and non-nil, it acts as the fn parameter of NewNotFoundResponder, allowing to dump the stack trace to localize the origin of the call.

import (
  "testing"
  "github.com/jarcoal/httpmock"
)
...
func TestMyApp(t *testing.T) {
  ...
  // This responder is callable 3 times, then an error is returned and
  // the stacktrace of the call logged using t.Log()
  httpmock.RegisterResponder("GET", "/foo/bar",
    httpmock.NewStringResponder(200, "{}").Times(3, t.Log),
  )

func (Responder) Trace added in v1.0.3

func (r Responder) Trace(fn func(...any)) Responder

Trace returns a new Responder that allows to easily trace the calls of the original Responder using fn. It can be used in conjunction with the testing package as in the example below with the help of *testing.T.Log method:

import (
  "testing"
  "github.com/jarcoal/httpmock"
)
...
func TestMyApp(t *testing.T) {
  ...
  httpmock.RegisterResponder("GET", "/foo/bar",
    httpmock.NewStringResponder(200, "{}").Trace(t.Log),
  )

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL