<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Patterns on gotest</title><link>https://mvrahden.github.io/go-test/tags/patterns/</link><description>Recent content in Patterns on gotest</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 06 Sep 2026 15:15:15 +0200</lastBuildDate><atom:link href="https://mvrahden.github.io/go-test/tags/patterns/feed.xml" rel="self" type="application/rss+xml"/><item><title>Contract Testing Go Interfaces with Generic Test Suites</title><link>https://mvrahden.github.io/go-test/blog/contract-testing-generics/</link><pubDate>Tue, 21 Jul 2026 00:00:00 +0000</pubDate><guid>https://mvrahden.github.io/go-test/blog/contract-testing-generics/</guid><description>&lt;p&gt;Go interfaces define contracts. An &lt;code&gt;io.Reader&lt;/code&gt; promises &lt;code&gt;Read(p []byte) (n int, err error)&lt;/code&gt;. A &lt;code&gt;Storage&lt;/code&gt; interface promises &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;Put&lt;/code&gt;, &lt;code&gt;Delete&lt;/code&gt;. But who verifies that every implementation actually honors the contract? Usually, each implementation has its own tests, written independently, checking slightly different things. The contract drifts.&lt;/p&gt;
&lt;p&gt;Generic suites fix this. Write one test suite parameterized by a type, then instantiate it for every implementation. The same behaviors are verified against every implementation. If you add a contract requirement, every implementation is tested. If an implementation deviates, the specific behavior that fails is named in the spec.&lt;/p&gt;</description></item><item><title>Snapshot Testing in Go</title><link>https://mvrahden.github.io/go-test/blog/snapshot-testing-in-go/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0000</pubDate><guid>https://mvrahden.github.io/go-test/blog/snapshot-testing-in-go/</guid><description>&lt;p&gt;Some test assertions are about structure, not specific values. JSON API responses, rendered HTML, error messages, log output — things where the exact expected value is long, tedious to maintain by hand, and changes often enough to be annoying. Snapshot testing solves this by storing the expected output in a file and comparing against it on subsequent runs.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve used Jest, you know the pattern. In Go, it&amp;rsquo;s less common — but &lt;a href="https://github.com/mvrahden/go-test"&gt;gotest&lt;/a&gt; has a built-in implementation that&amp;rsquo;s thread-safe, works inside parallel tests, and integrates with CI safety guards.&lt;/p&gt;</description></item><item><title>Testing Async Code in Go Without time.Sleep</title><link>https://mvrahden.github.io/go-test/blog/testing-async-go/</link><pubDate>Thu, 16 Jul 2026 00:00:00 +0000</pubDate><guid>https://mvrahden.github.io/go-test/blog/testing-async-go/</guid><description>&lt;p&gt;Most Go code that tests async behavior uses &lt;code&gt;time.Sleep&lt;/code&gt;. It&amp;rsquo;s slow, flaky, or both. The real problem is that sleeping conflates two separate concerns: how long to wait and how often to check. This post shows how to separate them with &lt;code&gt;Eventually&lt;/code&gt; and &lt;code&gt;Consistently&lt;/code&gt;, and why that separation makes async tests both faster and more reliable.&lt;/p&gt;
&lt;h2 id="the-sleep-problem"&gt;The sleep problem&lt;/h2&gt;
&lt;p&gt;Suppose you have a notification dispatcher that processes messages in a background goroutine. You send a notification and want to assert that it was delivered. The most common approach:&lt;/p&gt;</description></item><item><title>Go Test Lifecycle: Setup, Teardown, and Execution Order</title><link>https://mvrahden.github.io/go-test/blog/go-test-lifecycle/</link><pubDate>Tue, 14 Jul 2026 00:00:00 +0000</pubDate><guid>https://mvrahden.github.io/go-test/blog/go-test-lifecycle/</guid><description>&lt;p&gt;Every test framework has setup and teardown. The interesting question is not &lt;em&gt;whether&lt;/em&gt; they exist, but what guarantees the lifecycle gives you. Can &lt;code&gt;AfterAll&lt;/code&gt; run if &lt;code&gt;BeforeAll&lt;/code&gt; panics? Does &lt;code&gt;AfterEach&lt;/code&gt; still execute on &lt;code&gt;t.Fatal&lt;/code&gt;? Where do fixture hooks fit relative to suite hooks? If you have ever guessed at these answers while debugging a leaked database connection, this post is for you.&lt;/p&gt;
&lt;p&gt;Other posts in this series cover &lt;a href="https://mvrahden.github.io/go-test/blog/test-fixtures-in-go/"&gt;fixture patterns&lt;/a&gt; and &lt;a href="https://mvrahden.github.io/go-test/blog/zero-to-suite/"&gt;getting started with suites&lt;/a&gt;. This post is different. It maps the full lifecycle from the inside out, so you build the correct mental model once and stop guessing.&lt;/p&gt;</description></item><item><title>Test Fixtures in Go: Patterns That Scale</title><link>https://mvrahden.github.io/go-test/blog/test-fixtures-in-go/</link><pubDate>Tue, 07 Jul 2026 00:00:00 +0000</pubDate><guid>https://mvrahden.github.io/go-test/blog/test-fixtures-in-go/</guid><description>&lt;p&gt;Most Go projects eventually need shared test infrastructure: a database connection, a message queue, a container that takes seconds to start. The stdlib doesn&amp;rsquo;t have a built-in answer for this. So every team invents its own fixture pattern, and most of those patterns quietly break the day you add &lt;code&gt;t.Parallel()&lt;/code&gt; or a second package that needs the same Postgres.&lt;/p&gt;
&lt;p&gt;This post looks at the common approaches, where each one breaks down, and what a fixture system needs to handle the cases that real projects run into.&lt;/p&gt;</description></item><item><title>Organizing Go Tests Beyond `func TestX`</title><link>https://mvrahden.github.io/go-test/blog/organizing-go-tests/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate><guid>https://mvrahden.github.io/go-test/blog/organizing-go-tests/</guid><description>&lt;p&gt;A test file with 10 test functions is easy to hold in your head. At 50, you&amp;rsquo;re scrolling to find what you need. At 200, you&amp;rsquo;re &lt;code&gt;grep&lt;/code&gt;-ing your own tests to figure out what&amp;rsquo;s covered and what isn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Go&amp;rsquo;s testing package is deliberately simple. No annotations, no test classes, no dependency injection: just functions that start with &lt;code&gt;Test&lt;/code&gt; and a &lt;code&gt;*testing.T&lt;/code&gt;. This simplicity is one of Go&amp;rsquo;s genuine strengths. But it creates a gap that every growing project eventually falls into.&lt;/p&gt;</description></item></channel></rss>