Top Related Projects
Quick Overview
httpmock is a Golang library for mocking HTTP responses in unit tests. It allows developers to easily simulate HTTP requests and responses without making actual network calls, making it ideal for testing HTTP clients and APIs.
Pros
- Easy to use and integrate into existing Go test suites
- Supports both simple and complex HTTP mocking scenarios
- Provides a clean and expressive API for defining mock responses
- Allows for dynamic response generation based on request parameters
Cons
- Limited to Go programming language
- May require additional setup for more complex testing scenarios
- Not suitable for end-to-end testing or integration testing with real HTTP servers
- Documentation could be more comprehensive for advanced use cases
Code Examples
- Basic HTTP GET request mocking:
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpmock.RegisterResponder("GET", "https://api.example.com/users",
httpmock.NewStringResponder(200, `{"users": [{"id": 1, "name": "John"}]}`))
resp, err := http.Get("https://api.example.com/users")
// Assert on resp and err
- Mocking with custom responder function:
httpmock.RegisterResponder("POST", "https://api.example.com/users",
func(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)
return httpmock.NewStringResponse(201, string(body)), nil
},
)
resp, err := http.Post("https://api.example.com/users", "application/json",
strings.NewReader(`{"name": "Alice"}`))
// Assert on resp and err
- Mocking with query parameter matching:
httpmock.RegisterResponder("GET", "https://api.example.com/users",
httpmock.NewQueryParamResponder(
"id", "123",
httpmock.NewStringResponder(200, `{"user": {"id": 123, "name": "Bob"}}`),
),
)
resp, err := http.Get("https://api.example.com/users?id=123")
// Assert on resp and err
Getting Started
To use httpmock in your Go project:
-
Install the library:
go get github.com/jarcoal/httpmock
-
Import it in your test file:
import "github.com/jarcoal/httpmock"
-
Activate httpmock at the beginning of your test and deactivate it at the end:
func TestMyFunction(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() // Your test code here }
-
Register mock responses and write your tests as shown in the code examples above.
Competitor Comparisons
HTTP traffic mocking and testing made easy in Go ༼ʘ̚ل͜ʘ̚༽
Pros of gock
- More expressive and flexible API for defining mocks
- Supports persistent mocks across test cases
- Offers built-in support for JSON schema validation
Cons of gock
- Slightly steeper learning curve due to more advanced features
- Less widespread adoption compared to httpmock
Code Comparison
httpmock:
httpmock.RegisterResponder("GET", "http://example.com",
httpmock.NewStringResponder(200, `{"message": "hello"}`))
gock:
gock.New("http://example.com").
Get("/").
Reply(200).
JSON(map[string]string{"message": "hello"})
Both httpmock and gock are popular HTTP mocking libraries for Go, offering similar core functionality. httpmock is known for its simplicity and ease of use, making it a great choice for straightforward mocking scenarios. On the other hand, gock provides a more feature-rich API, allowing for more complex and flexible mock definitions.
gock's persistent mocks can be particularly useful in larger test suites, reducing the need to redefine mocks for each test case. Its JSON schema validation feature is also a significant advantage for projects working with complex JSON responses.
While gock's advanced features may require a bit more time to master, they can lead to more robust and maintainable test code in the long run. However, httpmock's simpler API and wider adoption might make it a better choice for smaller projects or teams new to HTTP mocking in Go.
Record and replay your HTTP interactions for fast, deterministic and accurate tests
Pros of go-vcr
- Records and replays HTTP interactions, allowing for more realistic testing scenarios
- Supports custom matchers for flexible request matching
- Can be used with any HTTP client that implements
http.RoundTripper
Cons of go-vcr
- Requires more setup and configuration compared to httpmock
- May introduce complexity when dealing with dynamic or time-sensitive data
- Slower test execution due to file I/O operations
Code Comparison
go-vcr:
recorder, err := vcr.New("fixtures/example")
defer recorder.Stop()
client := &http.Client{
Transport: recorder,
}
httpmock:
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpmock.RegisterResponder("GET", "http://example.com",
httpmock.NewStringResponder(200, "Hello, world!"))
Key Differences
-
Approach: go-vcr records and replays actual HTTP interactions, while httpmock simulates HTTP responses without making real requests.
-
Flexibility: go-vcr offers more realistic testing scenarios, but httpmock provides easier setup and faster test execution.
-
Use cases: go-vcr is better suited for integration tests and scenarios where exact API responses are crucial, while httpmock is ideal for unit testing and mocking simple HTTP responses.
-
Performance: httpmock generally offers faster test execution due to in-memory operations, whereas go-vcr may be slower due to file I/O.
-
Learning curve: httpmock has a simpler API and is easier to get started with, while go-vcr requires more configuration and understanding of its concepts.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
httpmock
Easy mocking of http responses from external resources.
Install
Currently supports Go 1.13 to 1.23 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
.
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:
http://example.tld/some/path?b=12&a=foo&a=bar
(original URL)http://example.tld/some/path?a=bar&a=foo&b=12
(sorted query params)http://example.tld/some/path
(without query params)/some/path?b=12&a=foo&a=bar
(original URL without scheme and host)/some/path?a=bar&a=foo&b=12
(same, but sorted query params)/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 ...
})
})
Top Related Projects
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot