apptest
packageAPI reference for the apptest
package.
Imports
(10)TestHost
TestHost provides a DI container and test HTTP server.
type TestHost struct
Methods
Close shuts down the test server and runs cleanup functions.
func (*TestHost) Close()
{
h.Server.Close()
_ = h.Container.Close()
for _, f := range h.cleanup {
f()
}
}
Cleanup registers a function to run when the host closes.
Parameters
func (*TestHost) Cleanup(fn func())
{
h.cleanup = append(h.cleanup, fn)
}
URL returns the full URL for the given path.
Parameters
Returns
func (*TestHost) URL(path string) string
{
return h.Server.URL + path
}
Get issues a GET request and returns a TestResponse.
Parameters
Returns
func (*TestHost) Get(path string) *TestResponse
{
resp, err := h.Client.Get(h.URL(path))
if err != nil {
return &TestResponse{Error: err}
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return &TestResponse{
StatusCode: resp.StatusCode,
Body: body,
Headers: resp.Header,
}
}
Post issues a POST request and returns a TestResponse.
Parameters
Returns
func (*TestHost) Post(path string, contentType string, body io.Reader) *TestResponse
{
resp, err := h.Client.Post(h.URL(path), contentType, body)
if err != nil {
return &TestResponse{Error: err}
}
defer resp.Body.Close()
responseBody, _ := io.ReadAll(resp.Body)
return &TestResponse{
StatusCode: resp.StatusCode,
Body: responseBody,
Headers: resp.Header,
}
}
Fields
| Name | Type | Description |
|---|---|---|
| Container | *di.Container | |
| Server | *httptest.Server | |
| Client | *http.Client | |
| cleanup | []func() |
NewTestHost
NewTestHost builds dependencies before registering routes against the final container.
Parameters
Returns
func NewTestHost(configure func(*di.Builder), register func(*web.Server, *di.Container) error) *TestHost
{
builder := di.NewBuilder()
server := web.New()
if configure != nil {
configure(builder)
}
container, err := builder.Build()
if err != nil {
panic(err)
}
if register != nil {
if err := register(server, container); err != nil {
_ = container.Close()
panic(err)
}
}
testServer := httptest.NewServer(server)
return &TestHost{
Container: container,
Server: testServer,
Client: testServer.Client(),
}
}
Resolve
Resolve retrieves a typed dependency from the container.
Parameters
Returns
func Resolve[T any](h *TestHost) T
{
return di.ResolveType[T](h.Container)
}
TestResponse
TestResponse holds an HTTP response for test assertions.
type TestResponse struct
Methods
Decode unmarshals the response body into v.
Parameters
Returns
func (*TestResponse) Decode(v any) error
{
return json.Unmarshal(r.Body, v)
}
String returns the response body as a string.
Returns
func (*TestResponse) String() string
{
return string(r.Body)
}
Fields
| Name | Type | Description |
|---|---|---|
| StatusCode | int | |
| Body | []byte | |
| Headers | http.Header | |
| Error | error |
FakeLogger
FakeLogger collects log entries for test assertions.
type FakeLogger struct
Methods
AssertLogged fails the test if msg was not logged.
Parameters
func (*FakeLogger) AssertLogged(t *testing.T, msg string)
{
t.Helper()
found := false
for _, e := range l.Entries {
if e == msg {
found = true
break
}
}
if !found {
t.Errorf("expected log %q, not found", msg)
}
}
Fields
| Name | Type | Description |
|---|---|---|
| Entries | []string |
NewFakeLogger
NewFakeLogger creates a FakeLogger.
Returns
func NewFakeLogger() *FakeLogger
{
return &FakeLogger{}
}
TestContext
TestContext holds a context and variable map for test use.
type TestContext struct
Fields
| Name | Type | Description |
|---|---|---|
| Ctx | context.Context | |
| Vars | map[string]any |
NewTestContext
NewTestContext creates a TestContext with a background context.
Returns
func NewTestContext() *TestContext
{
return &TestContext{
Ctx: context.Background(),
Vars: make(map[string]any),
}
}
testService
type testService struct
Fields
| Name | Type | Description |
|---|---|---|
| Name | string |
closeTracker
type closeTracker struct
Methods
Fields
| Name | Type | Description |
|---|---|---|
| closed | bool |
TestTestHostHTTPAndResolve
Parameters
func TestTestHostHTTPAndResolve(t *testing.T)
{
host := NewTestHost(func(b *di.Builder) {
di.RegisterInstance[*testService](b, &testService{Name: "svc"})
}, func(app *web.Server, container *di.Container) error {
app.MapGet("/ping", func(ctx *web.Context) error {
return ctx.JSON(201, map[string]string{"ok": "yes"})
})
app.MapPost("/echo", func(ctx *web.Context) error {
var body map[string]string
if err := ctx.Bind(&body); err != nil {
return err
}
return ctx.JSON(200, body)
})
return nil
})
defer host.Close()
service := Resolve[*testService](host)
if service.Name != "svc" {
t.Fatalf("Resolve() name = %q, want svc", service.Name)
}
resp := host.Get("/ping")
if resp.Error != nil {
t.Fatalf("Get() error = %v", resp.Error)
}
if resp.StatusCode != 201 {
t.Fatalf("Get() status = %d, want 201", resp.StatusCode)
}
var decoded map[string]string
if err := resp.Decode(&decoded); err != nil {
t.Fatalf("Decode() error = %v", err)
}
if decoded["ok"] != "yes" {
t.Fatalf("Decode()[ok] = %q, want yes", decoded["ok"])
}
post := host.Post("/echo", "application/json", strings.NewReader(`{"msg":"hello"}`))
if post.StatusCode != 200 {
t.Fatalf("Post() status = %d, want 200", post.StatusCode)
}
}
TestTestHostCleanup
Parameters
func TestTestHostCleanup(t *testing.T)
{
host := NewTestHost(nil, nil)
called := false
host.Cleanup(func() { called = true })
host.Close()
if !called {
t.Fatal("cleanup was not called")
}
}
TestFakeLoggerAssertLogged
Parameters
func TestFakeLoggerAssertLogged(t *testing.T)
{
logger := NewFakeLogger()
logger.Entries = append(logger.Entries, "ready")
logger.AssertLogged(t, "ready")
}
TestTestContext
Parameters
func TestTestContext(t *testing.T)
{
ctx := NewTestContext()
ctx.Vars["key"] = "value"
if ctx.Ctx == nil {
t.Fatal("Ctx is nil")
}
if ctx.Vars["key"] != "value" {
t.Fatalf("Vars[key] = %v, want value", ctx.Vars["key"])
}
}
TestNewTestHostClosesContainerWhenRegistrationFails
Parameters
func TestNewTestHostClosesContainerWhenRegistrationFails(t *testing.T)
{
tracker := &closeTracker{}
defer func() {
if recover() == nil {
t.Fatal("NewTestHost() did not panic")
}
if !tracker.closed {
t.Fatal("NewTestHost() leaked the built container")
}
}()
NewTestHost(func(builder *di.Builder) {
di.RegisterInstance[io.Closer](builder, tracker)
}, func(*web.Server, *di.Container) error {
return errors.New("registration failed")
})
}