...

Text file src/github.com/jarcoal/httpmock/README.md

Documentation: github.com/jarcoal/httpmock

     1# httpmock [![Build Status](https://github.com/jarcoal/httpmock/workflows/Build/badge.svg?branch=v1)](https://github.com/jarcoal/httpmock/actions?query=workflow%3ABuild) [![Coverage Status](https://coveralls.io/repos/github/jarcoal/httpmock/badge.svg?branch=v1)](https://coveralls.io/github/jarcoal/httpmock?branch=v1) [![GoDoc](https://godoc.org/github.com/jarcoal/httpmock?status.svg)](https://godoc.org/github.com/jarcoal/httpmock) [![Version](https://img.shields.io/github/tag/jarcoal/httpmock.svg)](https://github.com/jarcoal/httpmock/releases) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go/#testing)
     2
     3Easy mocking of http responses from external resources.
     4
     5## Install
     6
     7Currently supports Go 1.9 - 1.18.
     8
     9`v1` branch has to be used instead of `master`.
    10
    11
    12### Using go modules (aka. `go mod`)
    13
    14In your go files, simply use:
    15```go
    16import "github.com/jarcoal/httpmock"
    17```
    18
    19Then next `go mod tidy` or `go test` invocation will automatically
    20populate your `go.mod` with the last httpmock release, now
    21[![Version](https://img.shields.io/github/tag/jarcoal/httpmock.svg)](https://github.com/jarcoal/httpmock/releases).
    22
    23Note you can use `go mod vendor` to vendor your dependencies.
    24
    25
    26### Using `$GOPATH`
    27
    28`v1` branch is configured as the default branch in github, so:
    29```
    30go get github.com/jarcoal/httpmock
    31```
    32
    33automatically downloads the `v1` branch in `$GOPATH/src`. Then in your
    34go files use:
    35```go
    36import "github.com/jarcoal/httpmock"
    37```
    38
    39
    40### Vendoring, using [`govendor`](https://github.com/kardianos/govendor) for example
    41
    42When vendoring is used, `v1` branch has to be specified. Two choices here:
    43
    44- preferred way:
    45  ```
    46  govendor fetch github.com/jarcoal/httpmock@v1
    47  ```
    48  then in go files:
    49  ```go
    50  import "github.com/jarcoal/httpmock"
    51  ```
    52- old way (before `v1` was set as default branch), use gopkg to read from
    53  `v1` branch:
    54  ```
    55  govendor fetch gopkg.in/jarcoal/httpmock.v1
    56  ```
    57  then in go files:
    58  ```go
    59  import "gopkg.in/jarcoal/httpmock.v1"
    60  ```
    61
    62
    63## Usage
    64
    65### Simple Example:
    66```go
    67func TestFetchArticles(t *testing.T) {
    68  httpmock.Activate()
    69  defer httpmock.DeactivateAndReset()
    70
    71  // Exact URL match
    72  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles",
    73    httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`))
    74
    75  // Regexp match (could use httpmock.RegisterRegexpResponder instead)
    76  httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/\d+\z`,
    77    httpmock.NewStringResponder(200, `{"id": 1, "name": "My Great Article"}`))
    78
    79  // do stuff that makes a request to articles
    80  ...
    81
    82  // get count info
    83  httpmock.GetTotalCallCount()
    84
    85  // get the amount of calls for the registered responder
    86  info := httpmock.GetCallCountInfo()
    87  info["GET https://api.mybiz.com/articles"] // number of GET calls made to https://api.mybiz.com/articles
    88  info["GET https://api.mybiz.com/articles/id/12"] // number of GET calls made to https://api.mybiz.com/articles/id/12
    89  info[`GET =~^https://api\.mybiz\.com/articles/id/\d+\z`] // number of GET calls made to https://api.mybiz.com/articles/id/<any-number>
    90}
    91```
    92
    93### Advanced Example:
    94```go
    95func TestFetchArticles(t *testing.T) {
    96  httpmock.Activate()
    97  defer httpmock.DeactivateAndReset()
    98
    99  // our database of articles
   100  articles := make([]map[string]interface{}, 0)
   101
   102  // mock to list out the articles
   103  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles",
   104    func(req *http.Request) (*http.Response, error) {
   105      resp, err := httpmock.NewJsonResponse(200, articles)
   106      if err != nil {
   107        return httpmock.NewStringResponse(500, ""), nil
   108      }
   109      return resp, nil
   110    },
   111  )
   112
   113  // return an article related to the request with the help of regexp submatch (\d+)
   114  httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/(\d+)\z`,
   115    func(req *http.Request) (*http.Response, error) {
   116      // Get ID from request
   117      id := httpmock.MustGetSubmatchAsUint(req, 1) // 1=first regexp submatch
   118      return httpmock.NewJsonResponse(200, map[string]interface{}{
   119        "id":   id,
   120        "name": "My Great Article",
   121      })
   122    },
   123  )
   124
   125  // mock to add a new article
   126  httpmock.RegisterResponder("POST", "https://api.mybiz.com/articles",
   127    func(req *http.Request) (*http.Response, error) {
   128      article := make(map[string]interface{})
   129      if err := json.NewDecoder(req.Body).Decode(&article); err != nil {
   130        return httpmock.NewStringResponse(400, ""), nil
   131      }
   132
   133      articles = append(articles, article)
   134
   135      resp, err := httpmock.NewJsonResponse(200, article)
   136      if err != nil {
   137        return httpmock.NewStringResponse(500, ""), nil
   138      }
   139      return resp, nil
   140    },
   141  )
   142
   143  // do stuff that adds and checks articles
   144}
   145```
   146
   147### Algorithm
   148
   149When `GET http://example.tld/some/path?b=12&a=foo&a=bar` request is
   150caught, all standard responders are checked against the following URL
   151or paths, the first match stops the search:
   152
   1531. `http://example.tld/some/path?b=12&a=foo&a=bar` (original URL)
   1541. `http://example.tld/some/path?a=bar&a=foo&b=12` (sorted query params)
   1551. `http://example.tld/some/path` (without query params)
   1561. `/some/path?b=12&a=foo&a=bar` (original URL without scheme and host)
   1571. `/some/path?a=bar&a=foo&b=12` (same, but sorted query params)
   1581. `/some/path` (path only)
   159
   160If no standard responder matched, the regexp responders are checked,
   161in the same order, the first match stops the search.
   162
   163
   164### [go-testdeep](https://go-testdeep.zetta.rocks/) + [tdsuite](https://pkg.go.dev/github.com/maxatome/go-testdeep/helpers/tdsuite) example:
   165```go
   166// article_test.go
   167
   168import (
   169  "testing"
   170
   171  "github.com/jarcoal/httpmock"
   172  "github.com/maxatome/go-testdeep/helpers/tdsuite"
   173  "github.com/maxatome/go-testdeep/td"
   174)
   175
   176type MySuite struct{}
   177
   178func (s *MySuite) Setup(t *td.T) error {
   179  // block all HTTP requests
   180  httpmock.Activate()
   181  return nil
   182}
   183
   184func (s *MySuite) PostTest(t *td.T, testName string) error {
   185  // remove any mocks after each test
   186  httpmock.Reset()
   187  return nil
   188}
   189
   190func (s *MySuite) Destroy(t *td.T) error {
   191  httpmock.DeactivateAndReset()
   192  return nil
   193}
   194
   195func TestMySuite(t *testing.T) {
   196  tdsuite.Run(t, &MySuite{})
   197}
   198
   199func (s *MySuite) TestArticles(assert, require *td.T) {
   200  httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles.json",
   201    httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`))
   202
   203  // do stuff that makes a request to articles.json
   204}
   205```
   206
   207
   208### [Ginkgo](https://onsi.github.io/ginkgo/) example:
   209```go
   210// article_suite_test.go
   211
   212import (
   213  // ...
   214  "github.com/jarcoal/httpmock"
   215)
   216// ...
   217var _ = BeforeSuite(func() {
   218  // block all HTTP requests
   219  httpmock.Activate()
   220})
   221
   222var _ = BeforeEach(func() {
   223  // remove any mocks
   224  httpmock.Reset()
   225})
   226
   227var _ = AfterSuite(func() {
   228  httpmock.DeactivateAndReset()
   229})
   230
   231
   232// article_test.go
   233
   234import (
   235  // ...
   236  "github.com/jarcoal/httpmock"
   237)
   238
   239var _ = Describe("Articles", func() {
   240  It("returns a list of articles", func() {
   241    httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles.json",
   242      httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Great Article"}]`))
   243
   244    // do stuff that makes a request to articles.json
   245  })
   246})
   247```
   248
   249### [Ginkgo](https://onsi.github.io/ginkgo/) + [Resty](https://github.com/go-resty/resty) Example:
   250```go
   251// article_suite_test.go
   252
   253import (
   254  // ...
   255  "github.com/jarcoal/httpmock"
   256  "github.com/go-resty/resty"
   257)
   258// ...
   259var _ = BeforeSuite(func() {
   260  // block all HTTP requests
   261  httpmock.ActivateNonDefault(resty.DefaultClient.GetClient())
   262})
   263
   264var _ = BeforeEach(func() {
   265  // remove any mocks
   266  httpmock.Reset()
   267})
   268
   269var _ = AfterSuite(func() {
   270  httpmock.DeactivateAndReset()
   271})
   272
   273
   274// article_test.go
   275
   276import (
   277  // ...
   278  "github.com/jarcoal/httpmock"
   279  "github.com/go-resty/resty"
   280)
   281
   282var _ = Describe("Articles", func() {
   283  It("returns a list of articles", func() {
   284    fixture := `{"status":{"message": "Your message", "code": 200}}`
   285    responder := httpmock.NewStringResponder(200, fixture)
   286    fakeUrl := "https://api.mybiz.com/articles.json"
   287    httpmock.RegisterResponder("GET", fakeUrl, responder)
   288
   289    // fetch the article into struct
   290    articleObject := &models.Article{}
   291    _, err := resty.R().SetResult(articleObject).Get(fakeUrl)
   292
   293    // do stuff with the article object ...
   294  })
   295})
   296```

View as plain text