Convert Figma logo to code with AI

jarcoal logohttpmock

HTTP mocking for Golang

1,907
102
1,907
5

Top Related Projects

2,079

HTTP traffic mocking and testing made easy in Go ༼ʘ̚ل͜ʘ̚༽

1,231

Record and replay your HTTP interactions for fast, deterministic and accurate tests

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

  1. 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
  1. 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
  1. 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:

  1. Install the library:

    go get github.com/jarcoal/httpmock
    
  2. Import it in your test file:

    import "github.com/jarcoal/httpmock"
    
  3. 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
    }
    
  4. Register mock responses and write your tests as shown in the code examples above.

Competitor Comparisons

2,079

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.

1,231

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

  1. Approach: go-vcr records and replays actual HTTP interactions, while httpmock simulates HTTP responses without making real requests.

  2. Flexibility: go-vcr offers more realistic testing scenarios, but httpmock provides easier setup and faster test execution.

  3. 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.

  4. Performance: httpmock generally offers faster test execution due to in-memory operations, whereas go-vcr may be slower due to file I/O.

  5. 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 Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

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.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 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 ...
  })
})