httpmock

package module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2023 License: MIT Imports: 25 Imported by: 2

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

[table of contents]

Install

go get go.nhat.io/httpmock

[table of contents]

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:

[table of contents]

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
[]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-"))

[table of contents]

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

[table of contents]

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.

[table of contents]

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.

[table of contents]

Custom Matcher

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

[table of contents]

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

[table of contents]

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

[table of contents]

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/httpmock"
)

func TestSimple(t *testing.T) {
	srv := httpmock.New(func(s *httpmock.Server) {
		s.ExpectPost("/users").
			WithBody(httpmock.JSON(`{"id": 42}`))
	})(t)

	// Your request and assertions.
}

or

package main

import (
	"testing"

	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	srv := httpmock.New(func(s *httpmock.Server) {
		s.ExpectPost("/users").
			WithBodyJSON(map[string]any{"id": 42})
	})(t)

	// Your request and assertions.
}

[table of contents]

Response Code

By default, the response code is 200. You can change it by using ReturnCode(code int)

For example:

package main

import (
	"testing"

	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	srv := httpmock.New(func(s *httpmock.Server) {
		s.ExpectPost("/users").
			ReturnCode(httpmock.StatusCreated)
	})(t)

	// Your request and assertions.
}

[table of contents]

Response Header

To send a header to client, there are 2 options:

  • ReturnHeader(key, value string): Send a single header.
  • ReturnHeaders(header map[string]string): Send multiple headers.

Of course the header is not sent right away when you write the expectation but later on when the request is handled.

For example:

package main

import (
	"testing"

	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	srv := httpmock.New(func(s *httpmock.Server) {
		s.ExpectGet("/").
			ReturnHeader("Content-Type", "application/json").
			Return(`{"id": 42}`)
	})(t)

	// Your request and assertions.
}

[table of contents]

Response Body

There are several ways to create a response for the request

Method Explanation Example
Return(v string,bytes,fmt.Stringer) Nothing fancy, the response is the given string Return("hello world")
Returnf(format string, args ...any) Same as Return(), but with support for formatting using fmt.Sprintf() Returnf("hello %s", "world")
ReturnJSON(v any) The response is the result of json.Marshal(v) ReturnJSON(map[string]string{"name": "john"})
ReturnFile(path string) The response is the content of given file, read by io.ReadFile() ReturnFile("resources/fixtures/result.json")
Run(func(r *http.Request) ([]byte, error)) Custom Logic See the example

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

[table of contents]

Execution Plan

The mocked HTTP server is created with the go.nhat.io/httpmock/planner.Sequence() by default, and it matches incoming requests sequentially. You can easily change this behavior to match your application execution by implementing the planner.Planner interface.

package planner

import (
	"net/http"

	"go.nhat.io/httpmock/request"
)

type Planner interface {
	// IsEmpty checks whether the planner has no expectation.
	IsEmpty() bool
	// Expect adds a new expectation.
	Expect(expect *request.Request)
	// Plan decides how a request matches an expectation.
	Plan(req *http.Request) (*request.Request, error)
	// Remain returns remain expectations.
	Remain() []*request.Request
	// Reset removes all the expectations.
	Reset()
}

Then use it with Server.WithPlanner(newPlanner) (see the ExampleMockServer_alwaysFailPlanner)

When the Server.Expect(), or Server.Expect[METHOD]() is called, the mocked server will prepare a request and sends it to the planner. If there is an incoming request, the server will call Planner.PLan() to find the expectation that matches the request and executes it.

[table of contents]

Examples

package main

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.nhat.io/httpmock"
)

func TestSimple(t *testing.T) {
	mockServer := httpmock.New(func(s *httpmock.Server) {
		s.Expect(httpmock.MethodGet, "/").
			Return("hello world!")
	})

	s := mockServer(t)

	code, _, body, _ := httpmock.DoRequest(t, httpmock.MethodGet, s.URL()+"/", nil, nil)

	expectedCode := httpmock.StatusOK
	expectedBody := []byte(`hello world!`)

	assert.Equal(t, expectedCode, code)
	assert.Equal(t, expectedBody, body)

	// Success
}

func TestCustomResponse(t *testing.T) {
	mockServer := httpmock.New(func(s *httpmock.Server) {
		s.Expect(httpmock.MethodPost, "/create").
			WithHeader("Authorization", "Bearer token").
			WithBody(`{"name":"John Doe"}`).
			After(time.Second).
			ReturnCode(httpmock.StatusCreated).
			ReturnJSON(map[string]any{
				"id":   1,
				"name": "John Doe",
			})
	})

	s := mockServer(t)

	requestHeader := map[string]string{"Authorization": "Bearer token"}
	requestBody := []byte(`{"name":"John Doe"}`)
	code, _, body, _ := httpmock.DoRequestWithTimeout(t, httpmock.MethodPost, s.URL()+"/create", requestHeader, requestBody, time.Second)

	expectedCode := httpmock.StatusCreated
	expectedBody := []byte(`{"id":1,"name":"John Doe"}`)

	assert.Equal(t, expectedCode, code)
	assert.Equal(t, expectedBody, body)

	// Success
}

func TestExpectationsWereNotMet(t *testing.T) {
	mockServer := httpmock.New(func(s *httpmock.Server) {
		s.Expect(httpmock.MethodGet, "/").
			Return("hello world!")

		s.Expect(httpmock.MethodPost, "/create").
			WithHeader("Authorization", "Bearer token").
			WithBody(`{"name":"John Doe"}`).
			After(time.Second).
			ReturnJSON(map[string]any{
				"id":   1,
				"name": "John Doe",
			})
	})

	s := mockServer(t)

	code, _, body, _ := httpmock.DoRequest(t, httpmock.MethodGet, s.URL()+"/", nil, nil)

	expectedCode := httpmock.StatusOK
	expectedBody := []byte(`hello world!`)

	assert.Equal(t, expectedCode, code)
	assert.Equal(t, expectedBody, body)

	// The test fails with
	// Error:      	Received unexpected error:
	//             	there are remaining expectations that were not met:
	//             	- POST /create
	//             	    with header:
	//             	        Authorization: Bearer token
	//             	    with body
	//             	        {"name":"John Doe"}
}

See more examples

[table of contents]

Donation

If this project help you reduce time to develop, you can give me a cup of coffee :)

[table of contents]

Paypal donation

paypal

       or scan this

[table of contents]

Documentation

Overview

Package httpmock provides functionalities for mocking http server.

Index

Examples

Constants

View Source
const (
	MethodGet     = http.MethodGet
	MethodHead    = http.MethodHead
	MethodPost    = http.MethodPost
	MethodPut     = http.MethodPut
	MethodPatch   = http.MethodPatch
	MethodDelete  = http.MethodDelete
	MethodConnect = http.MethodConnect
	MethodOptions = http.MethodOptions
	MethodTrace   = http.MethodTrace

	StatusContinue                      = http.StatusContinue
	StatusSwitchingProtocols            = http.StatusSwitchingProtocols
	StatusProcessing                    = http.StatusProcessing
	StatusEarlyHints                    = http.StatusEarlyHints
	StatusOK                            = http.StatusOK
	StatusCreated                       = http.StatusCreated
	StatusAccepted                      = http.StatusAccepted
	StatusNonAuthoritativeInfo          = http.StatusNonAuthoritativeInfo
	StatusNoContent                     = http.StatusNoContent
	StatusResetContent                  = http.StatusResetContent
	StatusPartialContent                = http.StatusPartialContent
	StatusMultiStatus                   = http.StatusMultiStatus
	StatusAlreadyReported               = http.StatusAlreadyReported
	StatusIMUsed                        = http.StatusIMUsed
	StatusMultipleChoices               = http.StatusMultipleChoices
	StatusMovedPermanently              = http.StatusMovedPermanently
	StatusFound                         = http.StatusFound
	StatusSeeOther                      = http.StatusSeeOther
	StatusNotModified                   = http.StatusNotModified
	StatusUseProxy                      = http.StatusUseProxy
	StatusTemporaryRedirect             = http.StatusTemporaryRedirect
	StatusPermanentRedirect             = http.StatusPermanentRedirect
	StatusBadRequest                    = http.StatusBadRequest
	StatusUnauthorized                  = http.StatusUnauthorized
	StatusPaymentRequired               = http.StatusPaymentRequired
	StatusForbidden                     = http.StatusForbidden
	StatusNotFound                      = http.StatusNotFound
	StatusMethodNotAllowed              = http.StatusMethodNotAllowed
	StatusNotAcceptable                 = http.StatusNotAcceptable
	StatusProxyAuthRequired             = http.StatusProxyAuthRequired
	StatusRequestTimeout                = http.StatusRequestTimeout
	StatusConflict                      = http.StatusConflict
	StatusGone                          = http.StatusGone
	StatusLengthRequired                = http.StatusLengthRequired
	StatusPreconditionFailed            = http.StatusPreconditionFailed
	StatusRequestEntityTooLarge         = http.StatusRequestEntityTooLarge
	StatusRequestURITooLong             = http.StatusRequestURITooLong
	StatusUnsupportedMediaType          = http.StatusUnsupportedMediaType
	StatusRequestedRangeNotSatisfiable  = http.StatusRequestedRangeNotSatisfiable
	StatusExpectationFailed             = http.StatusExpectationFailed
	StatusTeapot                        = http.StatusTeapot
	StatusMisdirectedRequest            = http.StatusMisdirectedRequest
	StatusUnprocessableEntity           = http.StatusUnprocessableEntity
	StatusLocked                        = http.StatusLocked
	StatusFailedDependency              = http.StatusFailedDependency
	StatusTooEarly                      = http.StatusTooEarly
	StatusUpgradeRequired               = http.StatusUpgradeRequired
	StatusPreconditionRequired          = http.StatusPreconditionRequired
	StatusTooManyRequests               = http.StatusTooManyRequests
	StatusRequestHeaderFieldsTooLarge   = http.StatusRequestHeaderFieldsTooLarge
	StatusUnavailableForLegalReasons    = http.StatusUnavailableForLegalReasons
	StatusInternalServerError           = http.StatusInternalServerError
	StatusNotImplemented                = http.StatusNotImplemented
	StatusBadGateway                    = http.StatusBadGateway
	StatusServiceUnavailable            = http.StatusServiceUnavailable
	StatusGatewayTimeout                = http.StatusGatewayTimeout
	StatusHTTPVersionNotSupported       = http.StatusHTTPVersionNotSupported
	StatusVariantAlsoNegotiates         = http.StatusVariantAlsoNegotiates
	StatusInsufficientStorage           = http.StatusInsufficientStorage
	StatusLoopDetected                  = http.StatusLoopDetected
	StatusNotExtended                   = http.StatusNotExtended
	StatusNetworkAuthenticationRequired = http.StatusNetworkAuthenticationRequired
)

nolint: revive,nolintlint

Variables

View Source
var Exact = matcher.Exact

Exact matches two objects by their exact values.

View Source
var Exactf = matcher.Exactf

Exactf matches two strings by the formatted expectation.

View Source
var IsEmpty = matcher.IsEmpty

IsEmpty checks whether the value is empty.

View Source
var IsNotEmpty = matcher.IsNotEmpty

IsNotEmpty checks whether the value is not empty.

View Source
var JSON = matcher.JSON

JSON matches two json strings with <ignore-diff> support.

View Source
var Len = matcher.Len

Len matches by the length of the value.

View Source
var Match = matcher.Match

Match returns a matcher according to its type.

View Source
var Regex = matcher.Regex

Regex matches two strings by using regex.

View Source
var RegexPattern = matcher.RegexPattern

RegexPattern matches two strings by using regex.

Functions

func AssertHeaderContains

func AssertHeaderContains(t test.T, headers, contains Header) bool

AssertHeaderContains asserts that the HTTP headers contains some specifics headers.

func DoRequest

func DoRequest(
	tb testing.TB,
	method, requestURI string,
	headers Header,
	body []byte,
) (int, map[string]string, []byte, time.Duration)

DoRequest calls DoRequestWithTimeout with 1 second timeout. nolint:thelper // It is called in DoRequestWithTimeout.

func DoRequestWithTimeout

func DoRequestWithTimeout(
	tb testing.TB,
	method, requestURI string,
	headers Header,
	body []byte,
	timeout time.Duration,
) (int, map[string]string, []byte, time.Duration)

DoRequestWithTimeout sends a simple HTTP requestExpectation for testing and returns the status code, response headers and response body along with the total execution time.

code, headers, body, _ = DoRequestWithTimeout(t, http.MethodGet, "/", map[string]string{}, nil, 0)

func FailResponse added in v0.10.0

func FailResponse(w http.ResponseWriter, format string, args ...any) error

FailResponse responds a failure to client.

Types

type Expectation added in v0.10.0

type Expectation interface {
	// WithHeader sets an expected header of the given request.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		WithHeader("foo", "bar")
	WithHeader(header string, value any) Expectation
	// WithHeaders sets a list of expected headers of the given request.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		WithHeaders(map[string]any{"foo": "bar"})
	WithHeaders(headers map[string]any) Expectation
	// WithBody sets the expected body of the given request. It could be []byte, string, fmt.Stringer, or a Matcher.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		WithBody("hello world!")
	WithBody(body any) Expectation
	// WithBodyf formats according to a format specifier and use it as the expected body of the given request.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		WithBodyf("hello %s", "john)
	WithBodyf(format string, args ...any) Expectation
	// WithBodyJSON marshals the object and use it as the expected body of the given request.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		WithBodyJSON(map[string]string{"foo": "bar"})
	//
	WithBodyJSON(v any) Expectation

	// ReturnCode sets the response code.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		ReturnCode(httpmock.StatusBadRequest)
	ReturnCode(code int) Expectation
	// ReturnHeader sets a response header.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		ReturnHeader("foo", "bar")
	ReturnHeader(header, value string) Expectation
	// ReturnHeaders sets a list of response headers.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		ReturnHeaders(httpmock.Header{"foo": "bar"})
	ReturnHeaders(headers Header) Expectation
	// Return sets the result to return to client.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		Return("hello world!")
	Return(v any) Expectation
	// Returnf formats according to a format specifier and use it as the result to return to client.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		Returnf("hello %s", "john")
	Returnf(format string, args ...any) Expectation
	// ReturnJSON marshals the object using json.Marshal and uses it as the result to return to client.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		ReturnJSON(map[string]string{"foo": "bar"})
	ReturnJSON(body any) Expectation
	// ReturnFile reads the file using ioutil.ReadFile and uses it as the result to return to client.
	//
	//	Server.Expect(httpmock.MethodGet, "/path").
	//		ReturnFile("resources/fixtures/response.txt")
	ReturnFile(filePath string) Expectation
	// Run sets the handler to handle a given request.
	//
	//	   Server.Expect(httpmock.MethodGet, "/path").
	//			Run(func(*http.Request) ([]byte, error) {
	//				return []byte("hello world!"), nil
	//			})
	Run(handle func(r *http.Request) ([]byte, error)) Expectation

	// Once indicates that the mock should only return the value once.
	//
	//	Server.Expect(http.MethodGet, "/path").
	//		Return("hello world!").
	//		Once()
	Once() Expectation
	// Twice indicates that the mock should only return the value twice.
	//
	//	Server.Expect(http.MethodGet, "/path").
	//		Return("hello world!").
	//		Twice()
	Twice() Expectation
	// UnlimitedTimes indicates that the mock should return the value at least once and there is no max limit in the
	// number of return.
	//
	//	Server.Expect(http.MethodGet, "/path").
	//		Return("hello world!").
	//		UnlimitedTimes()
	UnlimitedTimes() Expectation
	// Times indicates that the mock should only return the indicated number of times.
	//
	//	Server.Expect(http.MethodGet, "/path").
	//		Return("hello world!").
	//		Times(5)
	Times(i uint) Expectation

	// WaitUntil sets the channel that will block the mocked return until its closed
	// or a message is received.
	//
	//	Server.Expect(http.MethodGet, "/path").
	//		WaitUntil(time.After(time.Second)).
	//		Return("hello world!")
	WaitUntil(w <-chan time.Time) Expectation
	// After sets how long to block until the call returns.
	//
	//	Server.Expect(http.MethodGet, "/path").
	//		After(time.Second).
	//		Return("hello world!")
	After(d time.Duration) Expectation
}

Expectation sets the expectations for a http request.

nolint: interfacebloat

type ExpectationHandler added in v0.11.0

type ExpectationHandler interface {
	Handle(http.ResponseWriter, *http.Request, map[string]string) error
}

ExpectationHandler handles the expectation.

type Header = map[string]string

Header is an alias of a string map.

type Mocker

type Mocker func(t test.T) *Server

Mocker is a function that applies expectations to the mocked server.

func New

func New(mocks ...func(s *Server)) Mocker

New creates a mocker server with expectations and assures that ExpectationsWereMet() is called.

s := httpmock.New(func(s *Server) {
	s.ExpectPost("/create").
		WithHeader("Authorization", "Bearer token").
		WithBody(`{"foo":"bar"}`).
		ReturnCode(httpmock.StatusCreated).
		Return(`{"id":1,"foo":"bar"}`)
})(t)

code, _, body, _ := httpmock.DoRequest(t,
	httpmock.MethodPost,
	s.URL()+"/create",
	map[string]string{"Authorization": "Bearer token"},
	[]byte(`{"foo":"bar"}`),
)

expectedCode := httpmock.StatusCreated
expectedBody := []byte(`{"id":1,"foo":"bar"}`)

assert.Equal(t, expectedCode, code)
assert.Equal(t, expectedBody, body)

type Server

type Server struct {
	// Requests are the matched expectations.
	Requests []planner.Expectation
	// contains filtered or unexported fields
}

Server is a Mock server.

func MockServer

func MockServer(mocks ...func(s *Server)) *Server

MockServer creates a mocked server.

Example (AlwaysFailPlanner)
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"

	"github.com/stretchr/testify/mock"

	"go.nhat.io/httpmock"

	plannermock "go.nhat.io/httpmock/mock/planner"
	"go.nhat.io/httpmock/must"
)

func main() {
	srv := httpmock.MockServer(func(s *httpmock.Server) {
		p := &plannermock.Planner{}

		p.On("IsEmpty").Return(false)
		p.On("Expect", mock.Anything)
		p.On("Plan", mock.Anything).
			Return(nil, errors.New("always fail"))

		s.WithPlanner(p)

		s.ExpectGet("/hi").
			Run(func(r *http.Request) ([]byte, error) {
				panic(`this never happens`)
			})
	})

	requestURI := srv.URL() + "/hi"
	req, err := http.NewRequestWithContext(context.Background(), httpmock.MethodGet, requestURI, nil)
	must.NotFail(err)

	resp, err := http.DefaultClient.Do(req)
	must.NotFail(err)

	defer resp.Body.Close() // nolint: errcheck

	output, err := io.ReadAll(resp.Body)
	must.NotFail(err)

	fmt.Println(resp.Status)
	fmt.Println(string(output))

}
Output:

500 Internal Server Error
always fail
Example (CustomHandle)
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"

	"go.nhat.io/httpmock"
	"go.nhat.io/httpmock/matcher"
	"go.nhat.io/httpmock/must"
)

func main() {
	srv := httpmock.MockServer(func(s *httpmock.Server) {
		s.ExpectGet(matcher.RegexPattern(`^/uri.*`)).
			WithHeader("Authorization", "Bearer token").
			Run(func(r *http.Request) ([]byte, error) {
				return []byte(r.RequestURI), nil
			})
	})

	requestURI := srv.URL() + "/uri?a=1&b=2"
	req, err := http.NewRequestWithContext(context.Background(), httpmock.MethodGet, requestURI, nil)
	must.NotFail(err)

	req.Header.Set("Authorization", "Bearer token")

	resp, err := http.DefaultClient.Do(req)
	must.NotFail(err)

	defer resp.Body.Close() // nolint: errcheck

	output, err := io.ReadAll(resp.Body)
	must.NotFail(err)

	fmt.Println(resp.Status)
	fmt.Println(string(output))

}
Output:

200 OK
/uri?a=1&b=2
Example (ExpectationsWereNotMet)
package main

import (
	"fmt"

	"go.nhat.io/httpmock"
)

func main() {
	srv := httpmock.MockServer(func(s *httpmock.Server) {
		s.ExpectGet("/hi").
			Return(`hello world`)

		s.ExpectGet("/pay").Twice().
			Return(`paid`)
	})

	err := srv.ExpectationsWereMet()

	fmt.Println(err.Error())

}
Output:

there are remaining expectations that were not met:
- GET /hi
- GET /pay (called: 0 time(s), remaining: 2 time(s))
Example (Simple)
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"

	"go.nhat.io/httpmock"
	"go.nhat.io/httpmock/must"
)

func main() {
	srv := httpmock.MockServer(func(s *httpmock.Server) {
		s.ExpectGet("/hi").
			Return(`hello world`)
	})

	requestURI := srv.URL() + "/hi"
	req, err := http.NewRequestWithContext(context.Background(), httpmock.MethodGet, requestURI, nil)
	must.NotFail(err)

	resp, err := http.DefaultClient.Do(req)
	must.NotFail(err)

	defer resp.Body.Close() // nolint: errcheck

	output, err := io.ReadAll(resp.Body)
	must.NotFail(err)

	fmt.Println(resp.Status)
	fmt.Println(string(output))

}
Output:

200 OK
hello world

func NewServer

func NewServer() *Server

NewServer creates a new server.

func (*Server) Close

func (s *Server) Close()

Close closes mocked server.

func (*Server) Expect

func (s *Server) Expect(method string, requestURI any) Expectation

Expect adds a new expected request.

Server.Expect(httpmock.MethodGet, "/path").

func (*Server) ExpectDelete

func (s *Server) ExpectDelete(requestURI any) Expectation

ExpectDelete adds a new expected http.MethodDelete request.

Server.ExpectDelete("/path")

func (*Server) ExpectGet

func (s *Server) ExpectGet(requestURI any) Expectation

ExpectGet adds a new expected http.MethodGet request.

Server.ExpectGet("/path")

func (*Server) ExpectHead

func (s *Server) ExpectHead(requestURI any) Expectation

ExpectHead adds a new expected http.MethodHead request.

Server.ExpectHead("/path")

func (*Server) ExpectPatch

func (s *Server) ExpectPatch(requestURI any) Expectation

ExpectPatch adds a new expected http.MethodPatch request.

Server.ExpectPatch("/path")

func (*Server) ExpectPost

func (s *Server) ExpectPost(requestURI any) Expectation

ExpectPost adds a new expected http.MethodPost request.

Server.ExpectPost("/path")

func (*Server) ExpectPut

func (s *Server) ExpectPut(requestURI any) Expectation

ExpectPut adds a new expected http.MethodPut request.

Server.ExpectPut("/path")

func (*Server) ExpectationsWereMet

func (s *Server) ExpectationsWereMet() error

ExpectationsWereMet checks whether all queued expectations were met in order. If any of them was not met - an error is returned.

func (*Server) ResetExpectations

func (s *Server) ResetExpectations()

ResetExpectations resets all the expectations.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP serves the request.

func (*Server) URL

func (s *Server) URL() string

URL returns the current URL of the httptest.Server.

func (*Server) WithDefaultRequestOptions

func (s *Server) WithDefaultRequestOptions(opt func(e Expectation)) *Server

WithDefaultRequestOptions sets the default request options of the server.

func (*Server) WithDefaultResponseHeaders

func (s *Server) WithDefaultResponseHeaders(headers map[string]string) *Server

WithDefaultResponseHeaders sets the default response headers of the server.

func (*Server) WithPlanner

func (s *Server) WithPlanner(p planner.Planner) *Server

WithPlanner sets the planner.

func (*Server) WithTest

func (s *Server) WithTest(t test.T) *Server

WithTest sets the *testing.T of the server.

Directories

Path Synopsis
Package format formats the requests.
Package format formats the requests.
Package matcher provides functionalities for matching header and body.
Package matcher provides functionalities for matching header and body.
mock
http
Package http provides functionalities for mocking net/http components.
Package http provides functionalities for mocking net/http components.
httpmock
Package httpmock provides functionalities for mocking the httpmock components.
Package httpmock provides functionalities for mocking the httpmock components.
planner
Package planner provides mock for planner.
Package planner provides mock for planner.
Package must panic on error.
Package must panic on error.
Package planner provides functionalities for deciding what expectation matches the given request.
Package planner provides functionalities for deciding what expectation matches the given request.
Package request provides all the expectations for a HTTP request.
Package request provides all the expectations for a HTTP request.
Package test provides functionalities for testing the project.
Package test provides functionalities for testing the project.
Package value provides functionalities to convert a generic value to string.
Package value provides functionalities to convert a generic value to string.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL