example

command
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2026 License: MIT Imports: 7 Imported by: 0

README

example

A greeting server that one form field changes. It exists so the domtest README can point at code that runs, and so the tests beside it have something real to assert on.

go run ./domtest/example   # serves on :8080
go test ./domtest/example

The server

main.go registers the routes and templates.gohtml renders them.

route answers with
GET / the whole page
POST / the whole page, after storing the submitted name
GET /greeting the heading alone, the kind of fragment a hypermedia library swaps into a page

An empty name greets the world and marks the heading default-value. Any other name is greeted by name and marks it custom-value. That class is the useful part, because it lets a test select the state directly instead of matching the greeting text, so the assertion survives a copy edit.

routes takes a StoreLoader rather than the concrete Storage, which is the seam a fake slots into.

The tests

main_test.go asserts on the result with the standard library alone. table_test.go does the same work table-driven with testify.

To see Read
A whole document parsed with no test framework TestGreetingPage in main_test.go
Given, When, and Then cases with testify TestRoutes in table_test.go
A form's method, action, and field values asserted the the form round-trips the stored name case
A marker class selected instead of the page's wording the a stored name is greeted instead case
A fragment parsed against its parent element the the greeting route answers with a fragment case
What the handler stored asserted alongside what it rendered the submitting a name stores it and greets it case

The shape of a handler suite

TestRoutes names its three phases so each case reads as a sentence about the server. Given arranges the world, When builds the request, Then asserts on the response. The phase types and runCase live inside the test function rather than in package scope, so another suite in the same package can reuse the names. runCase owns the setup for every case, which leaves the table readable as a list of behaviors. Each case gets a fresh mux over fresh storage, both local to the call, so cases stay independent and there is nothing to tear down.

Where a value lives follows from how many cases need it. Anything every case supplies or returns belongs in the Case field signatures, which is why When returns a *http.Request and Then receives a *http.Response. Anything only some cases need belongs in the Given, When, and Then structs. A case wanting one more value gets a field, not a new parameter on every case that does not need it. Naming each Case field after the struct it receives keeps the coupling easier to see.

Handing the storage to Then as well as Given lets a case assert on what the handler wrote. Both phases hold an interface, so a suite with a real backend swaps in a fake. In practice, you usually want to use a static type as the field type for phase fields. Don't introduce interface types just for tests.

Documentation

Overview

Command example serves a greeting that one form field changes. It exists so the domtest README can point at code that runs, and so the tests beside it have something real to assert on.

The server has three routes. GET / and POST / answer with a whole document, while GET /greeting answers with the heading alone, the kind of fragment a hypermedia library swaps into a page. An empty name greets the world and marks the heading default-value. Any other name is greeted by name and marks the heading custom-value, so a test selects the state by class instead of by wording.

main_test.go asserts on the result with the standard library alone. table_test.go does the same work table-driven with testify.

Jump to

Keyboard shortcuts

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