Skip to content
/ httpstub Public

httpstub provides router ( http.Handler ), server ( *httptest.Server ) and client ( *http.Client ) for stubbing, for testing in Go.

License

Notifications You must be signed in to change notification settings

k1LoW/httpstub

Folders and files

NameName
Last commit message
Last commit date

Latest commit

78cba51 · Apr 21, 2025
Mar 10, 2025
Mar 20, 2023
Aug 31, 2024
Feb 2, 2023
Apr 14, 2025
May 17, 2022
Mar 17, 2023
Apr 21, 2025
May 17, 2022
May 17, 2022
Mar 17, 2023
Dec 10, 2023
Apr 20, 2025
Apr 20, 2025
Apr 14, 2025
Nov 25, 2024
Feb 17, 2025
Aug 31, 2024
Apr 14, 2025

Repository files navigation

httpstub Go Reference Coverage Code to Test Ratio Test Execution Time

httpstub provides router ( http.Handler ), server ( *httptest.Server ) and client ( *http.Client ) for stubbing, for testing in Go.

There is an gRPC version stubbing tool with the same design concept, grpcstub.

Usage

package myapp

import (
	"io"
	"net/http"
	"testing"

	"github.com/k1LoW/httpstub"
)

func TestGet(t *testing.T) {
	ts := httpstub.NewServer(t)
	t.Cleanup(func() {
		ts.Close()
	})
	ts.Method(http.MethodGet).Path("/api/v1/users/1").Header("Content-Type", "application/json").ResponseString(http.StatusOK, `{"name":"alice"}`)

	res, err := http.Get(ts.URL + "/api/v1/users/1")
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() {
		res.Body.Close()
	})
	body, err := io.ReadAll(res.Body)
	if err != nil {
		t.Fatal(err)
	}
	got := string(body)
	want := `{"name":"alice"}`
	if got != want {
		t.Errorf("got %v\nwant %v", got, want)
	}
	if len(ts.Requests()) != 1 {
		t.Errorf("got %v\nwant %v", len(ts.Requests()), 1)
	}
}

or

package myapp

import (
	"io"
	"net/http"
	"testing"

	"github.com/k1LoW/httpstub"
)

func TestGet(t *testing.T) {
	r := httpstub.NewRouter(t)
	r.Method(http.MethodGet).Path("/api/v1/users/1").Header("Content-Type", "application/json").ResponseString(http.StatusOK, `{"name":"alice"}`)
	ts := r.Server()
	t.Cleanup(func() {
		ts.Close()
	})

	res, err := http.Get(ts.URL + "/api/v1/users/1")
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() {
		res.Body.Close()
	})
	body, err := io.ReadAll(res.Body)
	if err != nil {
		t.Fatal(err)
	}
	got := string(body)
	want := `{"name":"alice"}`
	if got != want {
		t.Errorf("got %v\nwant %v", got, want)
	}
	if len(r.Requests()) != 1 {
		t.Errorf("got %v\nwant %v", len(r.Requests()), 1)
	}
}

Response using examples: of OpenAPI Document

httpstub can return responses using examples: of OpenAPI Document.

Use examples: in all responses

ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"))
t.Cleanup(func() {
	ts.Close()
})
ts.ResponseExample()

Use examples: in response to specific endpoint

ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"))
t.Cleanup(func() {
	ts.Close()
})
ts.Method(http.MethodGet).Path("/api/v1/users/1").ResponseExample()

Use specific status code examples: in the response

It is possible to specify status codes using wildcard.

ts := httpstub.NewServer(t, httpstub.OpenApi3("path/to/schema.yml"))
t.Cleanup(func() {
	ts.Close()
})
ts.Method(http.MethodPost).Path("/api/v1/users").ResponseExample(httpstub.Status("2*"))

HTTP Client that always makes HTTP request to stub server

It is possible to create a client that will always make an HTTP request to the stub server.

ts := httpstub.NewServer(t)
t.Cleanup(func() {
	ts.Close()
})
ts.Method(http.MethodGet).Path("/api/v1/users/1").Header("Content-Type", "application/json").ResponseString(http.StatusOK, `{"name":"alice"}`)
tc := ts.Client()

res, err := tc.Get("https://example.com/api/v1/users/1") // Request goes to stub server instead of https://example.com
if err != nil {
	t.Fatal(err)
}

Example

Stub Twilio

package client_test

import (
	"net/http"
	"testing"

	"github.com/k1LoW/httpstub"
	twilio "github.com/twilio/twilio-go"
	twclient "github.com/twilio/twilio-go/client"
	api "github.com/twilio/twilio-go/rest/api/v2010"
)

func TestTwilioClient(t *testing.T) {
	r := httpstub.NewRouter(t)
	r.Method(http.MethodPost).Path("/2010-04-01/Accounts/*/Messages.json").ResponseString(http.StatusCreated, `{"status":"sending"}`)
	ts := r.Server()
	t.Cleanup(func() {
		ts.Close()
	})
	tc := ts.Client()

	accountSid := "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
	authToken := "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
	client := twilio.NewRestClientWithParams(twilio.ClientParams{
		Client: &twclient.Client{
			Credentials: twclient.NewCredentials(accountSid, authToken),
			HTTPClient:  tc,
		},
	})
	params := &api.CreateMessageParams{}
	params.SetTo("08000000000")
	params.SetFrom("05000000000")
	params.SetBody("Hello there")
	res, err := client.ApiV2010.CreateMessage(params)
	if err != nil {
		t.Error(err)
	}

	got := res.Status
	want := "sending"
	if *got != want {
		t.Errorf("got %v\nwant %v", *got, want)
	}
}

Alternatives