SkillAgentSearch skills...

Httpmock

HTTP Mock for Golang

Install / Use

/learn @nhatthm/Httpmock
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

⚠️ From v0.9.0, the project will be rebranded to go.nhat.io/httpmock. v.8.x is the last version with github.com/nhatthm/httpmock.

HTTP Mock for Golang

GitHub Releases Build Status codecov Go Report Card GoDevDoc Donate

httpmock is a mock library implementing httptest.Server to support HTTP behavioral tests.

Table of Contents

Prerequisites

  • Go >= 1.18

<sub><sup>[table of contents]</sup></sub>

Install

go get go.nhat.io/httpmock

<sub><sup>[table of contents]</sup></sub>

Usage

In a nutshell, the httpmock.Server is wrapper around httptest.Server. It provides extremely powerful methods to write complex expectations and test scenarios.

For creating a basic server, you can use httpmock.NewServer(). It starts a new HTTP server, and you can write your expectations right away.

However, if you use it in a test (with a t *testing.T), and want to stop the test when an error occurs (for example, unexpected requests, can't read request body, etc...), use Server.WithTest(t). At the end of the test, you can use Server.ExpectationsWereMet() error to check if the server serves all the expectation and there is nothing left. The approach is similar to stretchr/testify. Also, you need to close the server with Server.Close(). Luckily, you don't have to do that for every test, there is httpmock.New() method to start a new server, call Server.ExpectationsWereMet() and close the server at the end of the test, automatically.

For example:

package main

import (
	"testing"

	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	srv := httpmock.New(func(s *httpmock.Server) {
		s.ExpectGet("/").
			Return("hello world!")
	})(t)

	// Your request and assertions.
	// The server is ready at `srv.URL()`
}

After starting the server, you can use Server.URL() to get the address of the server.

For test table approach, you can use the Server.Mocker, example:

package main

import (
	"testing"

	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	testCases := []struct {
		scenario   string
		mockServer httpmock.Mocker
		// other input and expectations.
	}{
		{
			scenario: "some scenario",
			mockServer: httpmock.New(func(s *httpmock.Server) {
				s.ExpectGet("/").
					Return("hello world!")
			}),
		},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.scenario, func(t *testing.T) {
			t.Parallel()

			srv := tc.mockServer(t)

			// Your request and assertions.
		})
	}
}

Further reading:

<sub><sup>[table of contents]</sup></sub>

Match a value

httpmock is using go.nhat.io/matcher for matching values and that makes httpmock more powerful and convenient than ever. When writing expectations for the header or the payload, you can use any kind of matchers for your needs.

For example, the Request.WithHeader(header string, value any) means you expect a header that matches a value, you can put any of these into the value:

| Type | Explanation | Example | |:----------------------:|:---------------------------------------|:------------------------------------------------------| | string<br/>[]byte | Match the exact string, case-sensitive | .WithHeader("locale", "en-US") | | *regexp.Regexp | Match using regexp.Regex.MatchString | .WithHeader("locale", regexp.MustCompile("^en-")) | | matcher.RegexPattern | Match using regexp.Regex.MatchString | .WithHeader("locale", matcher.RegexPattern("^en-")) |

<sub><sup>[table of contents]</sup></sub>

Exact

matcher.Exact matches a value by using testify/assert.ObjectsAreEqual().

| Matcher | Input | Result | |:-------------------------------:|:-----------------:|:-------:| | matcher.Exact("en-US") | "en-US" | true | | matcher.Exact("en-US") | "en-us" | false | | matcher.Exact([]byte("en-US)) | []byte("en-US") | true | | matcher.Exact([]byte("en-US)) | "en-US" | false |

<sub><sup>[table of contents]</sup></sub>

Regexp

matcher.Regex and matcher.RegexPattern match a value by using Regexp.MatchString. matcher.Regex expects a *regexp.Regexp while matcher.RegexPattern expects only a regexp pattern. However, in the end, they are the same because nhatthm/go-matcher creates a new *regexp.Regexp from the pattern using regexp.MustCompile(pattern).

Notice, if the given input is not a string or []byte, the matcher always fails.

<sub><sup>[table of contents]</sup></sub>

JSON

matcher.JSON matches a value by using swaggest/assertjson.FailNotEqual. The matcher will marshal the input if it is not a string or a []byte, and then check against the expectation. For example, the expectation is matcher.JSON(`{"message": "hello"}`)

These inputs match that expectation:

  • {"message":"hello"} (notice there is no space after the : and it still matches)
  • []byte(`{"message":"hello"}`)
  • map[string]string{"message": "hello"}
  • Or any objects that produce the same JSON object after calling json.Marshal()

You could also ignore some fields that you don't want to match. For example, the expectation is matcher.JSON(`{"name": "John Doe"}`).If you match it with {"name": "John Doe", "message": "hello"}, that will fail because the message is unexpected. Therefore, use matcher.JSON(`{"name": "John Doe", "message": "<ignore-diff>"}`)

The "<ignore-diff>" can be used against any data types, not just the string. For example, {"id": "<ignore-diff>"} and {"id": 42} is a match.

<sub><sup>[table of contents]</sup></sub>

Custom Matcher

You can use your own matcher as long as it implements the matcher.Matcher interface.

<sub><sup>[table of contents]</sup></sub>

Expect a request

Request URI

Use the Server.Expect(method string, requestURI any), or Server.Expect[METHOD](requestURI any) to start a new expectation. You can put a string, a []byte or a matcher.Matcher for the requestURI. If the value is a string or a []byte, the URI is checked by using the matcher.Exact.

For example:

package main

import (
	"testing"

	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	srv := httpmock.New(func(s *httpmock.Server) {
		s.ExpectGet("/").
			Return("hello world!")
	})(t)

	// Your request and assertions.
}

<sub><sup>[table of contents]</sup></sub>

Request Header

To check whether the header of the incoming request matches some values. You can use:

  • Request.WithHeader(key string, value any): to match a single header.
  • Request.WithHeaders(header map[string]any): to match multiple headers.

The value could be string, []byte, or a matcher.Matcher. If the value is a string or a []byte, the header is checked by using the matcher.Exact.

For example:

package main

import (
	"testing"

	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	srv := httpmock.New(func(s *httpmock.Server) {
		s.ExpectGet("/").
			WithHeader("Authorization", httpmock.RegexPattern("^Bearer "))
	})(t)

	// Your request and assertions.
}

<sub><sup>[table of contents]</sup></sub>

Request Body

There are several ways to match a request body:

  • WithBody(body any): The expected body can be a string, a []byte or a matcher.Matcher . If it is a string or a []byte, the request body is checked by matched.Exact.
  • WithBodyf(format string, args ...any): Old school fmt.Sprintf() call, the request body is checked by matched.Exact with the result from fmt.Sprintf().
  • WithBodyJSON(body any): The expected body will be marshaled using json.Marshal() and the request body is checked by matched.JSON.

For example:

package main

import (
	"testing"

	"go.nhat.io/http

Related Skills

View on GitHub
GitHub Stars12
CategoryDevelopment
Updated10mo ago
Forks1

Languages

Go

Security Score

87/100

Audited on May 10, 2025

No findings