{"id":13413922,"url":"https://github.com/jarcoal/httpmock","last_synced_at":"2025-04-10T18:09:27.870Z","repository":{"id":14431754,"uuid":"17143026","full_name":"jarcoal/httpmock","owner":"jarcoal","description":"HTTP mocking for Golang","archived":false,"fork":false,"pushed_at":"2024-04-13T15:12:06.000Z","size":259,"stargazers_count":1863,"open_issues_count":5,"forks_count":103,"subscribers_count":10,"default_branch":"v1","last_synced_at":"2024-04-14T14:26:10.030Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://godoc.org/github.com/jarcoal/httpmock","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jarcoal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2014-02-24T16:47:59.000Z","updated_at":"2024-04-18T19:37:16.599Z","dependencies_parsed_at":"2024-04-18T19:47:22.416Z","dependency_job_id":null,"html_url":"https://github.com/jarcoal/httpmock","commit_stats":{"total_commits":109,"total_committers":23,"mean_commits":4.739130434782608,"dds":"0.39449541284403666","last_synced_commit":"1ddb9fa5c42c84afdca30c98914b42fda3005a57"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarcoal%2Fhttpmock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarcoal%2Fhttpmock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarcoal%2Fhttpmock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jarcoal%2Fhttpmock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jarcoal","download_url":"https://codeload.github.com/jarcoal/httpmock/tar.gz/refs/heads/v1","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248267988,"owners_count":21075491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:52.792Z","updated_at":"2025-04-10T18:09:27.865Z","avatar_url":"https://github.com/jarcoal.png","language":"Go","readme":"# httpmock [![Build Status](https://github.com/jarcoal/httpmock/actions/workflows/ci.yml/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)\n\nEasy mocking of http responses from external resources.\n\n## Install\n\nCurrently supports Go 1.16 to 1.24 and is regularly tested against tip.\n\n`v1` branch has to be used instead of `master`.\n\nIn your go files, simply use:\n```go\nimport \"github.com/jarcoal/httpmock\"\n```\n\nThen next `go mod tidy` or `go test` invocation will automatically\npopulate your `go.mod` with the latest httpmock release, now\n[![Version](https://img.shields.io/github/tag/jarcoal/httpmock.svg)](https://github.com/jarcoal/httpmock/releases).\n\n\n## Usage\n\n### Simple Example:\n```go\nfunc TestFetchArticles(t *testing.T) {\n  httpmock.Activate(t)\n\n  // Exact URL match\n  httpmock.RegisterResponder(\"GET\", \"https://api.mybiz.com/articles\",\n    httpmock.NewStringResponder(200, `[{\"id\": 1, \"name\": \"My Great Article\"}]`))\n\n  // Regexp match (could use httpmock.RegisterRegexpResponder instead)\n  httpmock.RegisterResponder(\"GET\", `=~^https://api\\.mybiz\\.com/articles/id/\\d+\\z`,\n    httpmock.NewStringResponder(200, `{\"id\": 1, \"name\": \"My Great Article\"}`))\n\n  // do stuff that makes a request to articles\n  ...\n\n  // get count info\n  httpmock.GetTotalCallCount()\n\n  // get the amount of calls for the registered responder\n  info := httpmock.GetCallCountInfo()\n  info[\"GET https://api.mybiz.com/articles\"] // number of GET calls made to https://api.mybiz.com/articles\n  info[\"GET https://api.mybiz.com/articles/id/12\"] // number of GET calls made to https://api.mybiz.com/articles/id/12\n  info[`GET =~^https://api\\.mybiz\\.com/articles/id/\\d+\\z`] // number of GET calls made to https://api.mybiz.com/articles/id/\u003cany-number\u003e\n}\n```\n\n### Advanced Example:\n```go\nfunc TestFetchArticles(t *testing.T) {\n  httpmock.Activate(t)\n\n  // our database of articles\n  articles := make([]map[string]interface{}, 0)\n\n  // mock to list out the articles\n  httpmock.RegisterResponder(\"GET\", \"https://api.mybiz.com/articles\",\n    func(req *http.Request) (*http.Response, error) {\n      resp, err := httpmock.NewJsonResponse(200, articles)\n      if err != nil {\n        return httpmock.NewStringResponse(500, \"\"), nil\n      }\n      return resp, nil\n    })\n\n  // return an article related to the request with the help of regexp submatch (\\d+)\n  httpmock.RegisterResponder(\"GET\", `=~^https://api\\.mybiz\\.com/articles/id/(\\d+)\\z`,\n    func(req *http.Request) (*http.Response, error) {\n      // Get ID from request\n      id := httpmock.MustGetSubmatchAsUint(req, 1) // 1=first regexp submatch\n      return httpmock.NewJsonResponse(200, map[string]interface{}{\n        \"id\":   id,\n        \"name\": \"My Great Article\",\n      })\n    })\n\n  // mock to add a new article\n  httpmock.RegisterResponder(\"POST\", \"https://api.mybiz.com/articles\",\n    func(req *http.Request) (*http.Response, error) {\n      article := make(map[string]interface{})\n      if err := json.NewDecoder(req.Body).Decode(\u0026article); err != nil {\n        return httpmock.NewStringResponse(400, \"\"), nil\n      }\n\n      articles = append(articles, article)\n\n      resp, err := httpmock.NewJsonResponse(200, article)\n      if err != nil {\n        return httpmock.NewStringResponse(500, \"\"), nil\n      }\n      return resp, nil\n    })\n\n  // mock to add a specific article, send a Bad Request response\n  // when the request body contains `\"type\":\"toy\"`\n  httpmock.RegisterMatcherResponder(\"POST\", \"https://api.mybiz.com/articles\",\n    httpmock.BodyContainsString(`\"type\":\"toy\"`),\n    httpmock.NewStringResponder(400, `{\"reason\":\"Invalid article type\"}`))\n\n  // do stuff that adds and checks articles\n}\n```\n\n### Algorithm\n\nWhen `GET http://example.tld/some/path?b=12\u0026a=foo\u0026a=bar` request is\ncaught, all standard responders are checked against the following URL\nor paths, the first match stops the search:\n\n1. `http://example.tld/some/path?b=12\u0026a=foo\u0026a=bar` (original URL)\n1. `http://example.tld/some/path?a=bar\u0026a=foo\u0026b=12` (sorted query params)\n1. `http://example.tld/some/path` (without query params)\n1. `/some/path?b=12\u0026a=foo\u0026a=bar` (original URL without scheme and host)\n1. `/some/path?a=bar\u0026a=foo\u0026b=12` (same, but sorted query params)\n1. `/some/path` (path only)\n\nIf no standard responder matched, the regexp responders are checked,\nin the same order, the first match stops the search.\n\n\n### [go-testdeep](https://go-testdeep.zetta.rocks/) + [tdsuite](https://pkg.go.dev/github.com/maxatome/go-testdeep/helpers/tdsuite) example:\n```go\n// article_test.go\n\nimport (\n  \"testing\"\n\n  \"github.com/jarcoal/httpmock\"\n  \"github.com/maxatome/go-testdeep/helpers/tdsuite\"\n  \"github.com/maxatome/go-testdeep/td\"\n)\n\ntype MySuite struct{}\n\nfunc (s *MySuite) Setup(t *td.T) error {\n  // block all HTTP requests\n  httpmock.Activate(t)\n  return nil\n}\n\nfunc (s *MySuite) PostTest(t *td.T, testName string) error {\n  // remove any mocks after each test\n  httpmock.Reset()\n  return nil\n}\n\nfunc TestMySuite(t *testing.T) {\n  tdsuite.Run(t, \u0026MySuite{})\n}\n\nfunc (s *MySuite) TestArticles(assert, require *td.T) {\n  httpmock.RegisterResponder(\"GET\", \"https://api.mybiz.com/articles.json\",\n    httpmock.NewStringResponder(200, `[{\"id\": 1, \"name\": \"My Great Article\"}]`))\n\n  // do stuff that makes a request to articles.json\n}\n```\n\n\n### [Ginkgo](https://onsi.github.io/ginkgo/) example:\n```go\n// article_suite_test.go\n\nimport (\n  // ...\n  \"github.com/jarcoal/httpmock\"\n)\n// ...\nvar _ = BeforeSuite(func() {\n  // block all HTTP requests\n  httpmock.Activate()\n})\n\nvar _ = BeforeEach(func() {\n  // remove any mocks\n  httpmock.Reset()\n})\n\nvar _ = AfterSuite(func() {\n  httpmock.DeactivateAndReset()\n})\n\n\n// article_test.go\n\nimport (\n  // ...\n  \"github.com/jarcoal/httpmock\"\n)\n\nvar _ = Describe(\"Articles\", func() {\n  It(\"returns a list of articles\", func() {\n    httpmock.RegisterResponder(\"GET\", \"https://api.mybiz.com/articles.json\",\n      httpmock.NewStringResponder(200, `[{\"id\": 1, \"name\": \"My Great Article\"}]`))\n\n    // do stuff that makes a request to articles.json\n  })\n})\n```\n\n### [Ginkgo](https://onsi.github.io/ginkgo/) + [Resty](https://github.com/go-resty/resty) Example:\n```go\n// article_suite_test.go\n\nimport (\n  // ...\n  \"github.com/jarcoal/httpmock\"\n  \"github.com/go-resty/resty/v2\"\n)\n// ...\n\n// global client (using resty.New() creates a new transport each time,\n// so you need to use the same one here and when making the request)\nvar client = resty.New()\n\nvar _ = BeforeSuite(func() {\n  // block all HTTP requests\n  httpmock.ActivateNonDefault(client.GetClient())\n})\n\nvar _ = BeforeEach(func() {\n  // remove any mocks\n  httpmock.Reset()\n})\n\nvar _ = AfterSuite(func() {\n  httpmock.DeactivateAndReset()\n})\n\n\n// article_test.go\n\nimport (\n  // ...\n  \"github.com/jarcoal/httpmock\"\n)\n\ntype Article struct {\n\tStatus struct {\n\t\tMessage string `json:\"message\"`\n\t\tCode    int    `json:\"code\"`\n\t} `json:\"status\"`\n}\n\nvar _ = Describe(\"Articles\", func() {\n  It(\"returns a list of articles\", func() {\n    fixture := `{\"status\":{\"message\": \"Your message\", \"code\": 200}}`\n    // have to use NewJsonResponder to get an application/json content-type\n    // alternatively, create a go object instead of using json.RawMessage\n    responder, _ := httpmock.NewJsonResponder(200, json.RawMessage(`{\"status\":{\"message\": \"Your message\", \"code\": 200}}`)\n    fakeUrl := \"https://api.mybiz.com/articles.json\"\n    httpmock.RegisterResponder(\"GET\", fakeUrl, responder)\n\n    // fetch the article into struct\n    articleObject := \u0026Article{}\n    _, err := resty.R().SetResult(articleObject).Get(fakeUrl)\n\n    // do stuff with the article object ...\n  })\n})\n```\n","funding_links":[],"categories":["Misc","开源类库","Testing","Go","Open source library","测试","Mock","测试相关`测试库和测试数据集生成库`","Template Engines","测试相关"],"sub_categories":["测试","Mock","Test","HTTP客户端","HTTP Clients","查询语"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarcoal%2Fhttpmock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjarcoal%2Fhttpmock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarcoal%2Fhttpmock/lists"}