Test a service that uses NATS¶
import "gitlab.com/phpboyscout/go/nats/natstest"
func TestSomething(t *testing.T) {
cli := natstest.Start(t)
// cli is a fully connected *client.Client. Cleanup is already registered.
}
That is the whole thing. No broker to install, no port, no credentials, and it runs in well under a second.
Nothing here is a fake¶
It is the real server and the real client. The only difference from production is that the transport is memory rather than a socket, so a test using it exercises the code that actually ships — including the bounded subscriptions, the shed reporting and the drain.
There is no mock of this module, and there should not be one. A fake NATS would be a second implementation of the behaviour the first one exists to get right.
Why it is a helper rather than a paragraph in this guide¶
The ordering is easy to get wrong and the failures do not name it as the cause:
- A client built before its server has started fails with
failed to get in-process connection. - A server stopped before its client has drained tears the connection out from under handlers that were still finishing.
natstest registers the server's cleanup first and the client's second, so cleanup runs in the
reverse of startup: the client drains, then the server stops. Getting that wrong is what this saves
you.
JetStream¶
cli := natstest.Start(t, natstest.WithJetStream())
stream, err := cli.Stream(t.Context(), client.StreamSettings{
Name: "orders", Subjects: []string{"orders.>"}, MaxAge: time.Hour,
})
The store directory is a t.TempDir(), so it never outlives the test that made it. Without
WithJetStream() the server has no store and creating a stream fails — which is worth knowing,
because the error says JetStream is not enabled rather than pointing at your test setup.
Observing sheds¶
sheds := make(chan client.Shed, 4)
cli := natstest.Start(t,
natstest.WithClientSettings(nats.ClientSettings{PendingMsgs: 1, PendingBytes: 1024}),
natstest.WithClientOptions(client.OnShed(func(s client.Shed) { sheds <- s })))
A tiny queue makes shedding easy to provoke. Send to a buffered channel: OnShed runs on the
connection's single callback goroutine, and blocking there starves the drain that Close waits for.
When you also need the server¶
For a test that builds a second client against the same server, or that exercises the server's own health probes.
The one thing to remember¶
Release any blocked handler before the test ends. Close waits for handlers — that is what it
is for — so a handler still blocked at cleanup costs the drain timeout and then reports
drain did not finish. Register the release with t.Cleanup after the harness call, so LIFO runs
it first: