Testing Web Apps
Introduction
An app is an http.Handler, so the standard library's httptest tests it. web.New(pages.Routes(), …) receives the generated page table, and each app has its own container and singletons, so every test can build a fresh one.
This page covers request tests, testing services directly, mail and crons, and live pages in a browser.
Testing Requests
This test serves the app with httptest and checks that an unknown pet answers 404:
func TestPets(t *testing.T) {
app := web.New()
app.Service(OpenDB)
srv := httptest.NewServer(app.Handler())
defer srv.Close()
res, err := http.Get(srv.URL + "/pets/9")
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != 404 {
t.Errorf("GET /pets/9: %d", res.StatusCode)
}
}Test files are _test.go; go test ./... runs them (go tool vuka test ./... when the package has .vuka.html templates).
Building the App in a Test
These methods and options set up the app a test checks:
app.Build() | every problem of the app — a test that the wiring is right, with no request |
app.Handler() | the handler; panics when Build fails |
web.Resolve[T](app) | the app's T, built with its dependencies: a service to call directly, or to seed |
app.Service(fakeConstructor) | a ready value replaces a constructor: provide the fake instead of the real one |
web.Only(pkgs…) | take only the declarations of these packages (import paths; "main" for the main package) |
web.NoDotEnv(), web.DotEnv(files…) | which env files a test reads; under go test, .env.test and .env |
See Dependency Injection and Configuration.
Testing a Service Directly
web.Resolve hands the test a service built with its dependencies, to call without a request:
func TestStore(t *testing.T) {
app := web.New()
app.Service(OpenDB)
store, err := web.Resolve[*PetStore](app)
if err != nil {
t.Fatal(err)
}
p, err := store.Add(NewPet{Name: "Rex"})
if err != nil {
t.Fatal(err)
}
t.Log("added", p.ID)
}Limiting a Test to Some Packages
web.Only matters when a test binary links packages that declare routes of their own: a library's tests, or an app whose packages each register a few.
Testing Mail and Crons
mailtest.New() is a Mailer that keeps what the app sends instead of sending it, and mailtest.Last(t, m) reads it back; see Mail. crontest.Run(app, name) runs a cron with its dependencies, and crontest.NewClock drives the schedule with a virtual clock; see Crons.
Testing Live Pages in a Browser
Live components are tested over HTTP for their first render (the page carries data-vk-token=), and in a browser for their events.
web's own tests drive a headless Chrome over the DevTools protocol and skip when none is found; VUKA_CHROME names the binary. See examples/live/main_test.vuka (its Go is what vuka eject writes).