Help us shape the future of Go! Take the 2026 Go Developer Survey

rtq

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Feb 19, 2026 License: MIT Imports: 9 Imported by: 0

README

go-rtq

A Go library for mocking HTTP requests with a queue-based RoundTrip implementation. Useful for testing HTTP clients without making real network calls.

API design and signatures reference gock.

Features

  • Queue-based mocking: Each request pattern has its own queue of mock responses; when a request matches, the response is consumed from that queue in FIFO order
  • Flexible request matching: Match by origin, method, path, headers, query parameters, and request body
  • Multiple response types: Return simple text, JSON, custom *http.Response, or custom handler functions
  • Thread-safe: Safe for concurrent use in parallel tests

Installation

go get github.com/goro9/go-rtq

Usage

Add the following imports to your test file:

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

    "github.com/goro9/go-rtq"
)
Basic Example
func TestBasicExample(t *testing.T) {
    q := rtq.New("http://api.example.com").
        Get("/users").
        ResponseSimple(200, `{"users": []}`).
        ResponseJSON(200, map[string]any{"users": []})

    transport := rtq.NewTransport(q)
    client := http.Client{Transport: transport}

    // First request returns {"users": []}
    resp1, err := client.Get("http://api.example.com/users")
    if err != nil {
        t.Fatal(err)
    }
    defer resp1.Body.Close()

    // Second request returns JSON response
    resp2, err := client.Get("http://api.example.com/users")
    if err != nil {
        t.Fatal(err)
    }
    defer resp2.Body.Close()

    // Third request fails with "mock is not registered"
    _, err = client.Get("http://api.example.com/users")
    if err == nil {
        t.Error("expected error: mock is not registered")
    }
}
Request Matching

Match requests by various criteria:

func TestRequestMatching(t *testing.T) {
    transport := rtq.NewTransport(
        rtq.New("http://api.example.com").Get("/users").ResponseSimple(200, "OK"),
        rtq.New("http://api.example.com").Post("/users").ResponseSimple(201, "Created"),
        rtq.New("http://api.example.com").
            Get("/users").
            Header("Authorization", "Bearer token123").
            ResponseSimple(200, "OK"),
        rtq.New("http://api.example.com").
            Get("/users").
            Query("page", "1").
            ResponseSimple(200, "OK"),
        rtq.New("http://api.example.com").
            Post("/users").
            BodyString(`{"name":"John"}`).
            ResponseSimple(200, "OK"),
        rtq.New("http://api.example.com").
            Matcher(func(req *http.Request) (bool, error) {
                return req.URL.Path == "/custom", nil
            }).
            ResponseSimple(200, "OK"),
    )
    client := http.Client{Transport: transport}
    // ... make requests and assert
}
Response Types
func TestResponseTypes(t *testing.T) {
    transport := rtq.NewTransport(
        rtq.New("http://api.example.com").
            Get("/simple").
            ResponseSimple(200, "OK"),
        rtq.New("http://api.example.com").
            Get("/json").
            ResponseJSON(200, map[string]any{"id": 1, "name": "Alice"}),
        rtq.New("http://api.example.com").
            Post("/custom").
            Response(&http.Response{
                StatusCode: 201,
                Body:       io.NopCloser(strings.NewReader("Created")),
            }),
        rtq.New("http://api.example.com").
            Get("/func").
            ResponseFunc(func(req *http.Request) (*http.Response, error) {
                return &http.Response{
                    StatusCode: 200,
                    Body:       io.NopCloser(strings.NewReader("custom")),
                    Request:    req,
                }, nil
            }),
    )
    client := http.Client{Transport: transport}
    // ... make requests and assert
}
Multiple Queues

Register multiple queues for different request patterns:

func TestMultipleQueues(t *testing.T) {
    transport := rtq.NewTransport(
        rtq.New("http://api.example.com").
            Get("/users").
            ResponseSimple(200, `{"users": []}`),
        rtq.New("http://api.example.com").
            Post("/users").
            ResponseJSON(201, map[string]any{"id": 1}),
        rtq.New("http://other.com").
            Get("/health").
            ResponseSimple(200, "OK"),
    )
    client := http.Client{Transport: transport}
    // ... make requests and assert
}
Verification

Check if all mocks were consumed and no unexpected requests were made:

func TestVerification(t *testing.T) {
    transport := rtq.NewTransport(
        rtq.New("http://api.example.com").Get("/users").ResponseSimple(200, "OK"),
    )
    client := http.Client{Transport: transport}

    resp, err := client.Get("http://api.example.com/users")
    if err != nil {
        t.Fatal(err)
    }
    defer resp.Body.Close()

    if !transport.Completed() {
        t.Error("expected all mocks to be consumed")
    }

    // Get request log string for debugging
    t.Log(transport.RequestLogString())
}

License

See LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type MatchFunc added in v0.0.4

type MatchFunc func(*http.Request) (bool, error)

type MockTransport added in v0.0.4

type MockTransport struct {
	// contains filtered or unexported fields
}

Have a RoundTrip queue for each specific request, and if the request matches, retrieve the RoundTrip from the queue and execute it.

func NewTransport

func NewTransport(queues ...RoundTripQueue) *MockTransport

func (*MockTransport) Completed added in v0.0.4

func (m *MockTransport) Completed() bool

func (*MockTransport) RequestLogString added in v0.0.5

func (m *MockTransport) RequestLogString() string

func (*MockTransport) RoundTrip added in v0.0.4

func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error)

type RoundTripQueue

type RoundTripQueue struct {
	// contains filtered or unexported fields
}

roundTrip queue

func New

func New(origin string) RoundTripQueue

func (RoundTripQueue) BodyString

func (q RoundTripQueue) BodyString(body string) RoundTripQueue

func (RoundTripQueue) Delete

func (q RoundTripQueue) Delete(path string) RoundTripQueue

func (RoundTripQueue) Get

func (q RoundTripQueue) Get(path string) RoundTripQueue

func (RoundTripQueue) Header

func (q RoundTripQueue) Header(key, value string) RoundTripQueue

func (RoundTripQueue) Matcher added in v0.0.4

func (q RoundTripQueue) Matcher(matchFunc MatchFunc) RoundTripQueue

func (RoundTripQueue) Post

func (q RoundTripQueue) Post(path string) RoundTripQueue

func (RoundTripQueue) Put

func (q RoundTripQueue) Put(path string) RoundTripQueue

func (RoundTripQueue) Query

func (q RoundTripQueue) Query(key, value string) RoundTripQueue

func (RoundTripQueue) Response

func (q RoundTripQueue) Response(res *http.Response) RoundTripQueue

func (RoundTripQueue) ResponseFunc

func (q RoundTripQueue) ResponseFunc(roundTrip func(*http.Request) (*http.Response, error)) RoundTripQueue

func (RoundTripQueue) ResponseJSON

func (q RoundTripQueue) ResponseJSON(statusCode int, body any) RoundTripQueue

func (RoundTripQueue) ResponseSimple

func (q RoundTripQueue) ResponseSimple(statusCode int, body string) RoundTripQueue

Jump to

Keyboard shortcuts

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