What Is Testo?
Testo is a modular Go testing framework built by OzonTech on top of testing.T, and it is one of the best Go Testing Frameworks tools for Go developers who have outgrown the standard library. At Ozon, Testo powers thousands of end-to-end tests daily in production, with a plugin model that adds suite lifecycle hooks, parameterization, parallel execution, and persistent caching without replacing the Go test runner.
Quick Overview
| Attribute | Details |
|---|---|
| Type | Go Testing Frameworks |
| Best For | Go developers, QA automation engineers, and platform teams |
| Language/Stack | Go, testing.T, suite-based testing, plugins, hooks, caching |
| License | Apache-2.0 |
| GitHub Stars | N/A as of Feb 2026 |
| Pricing | Open-Source |
| Last Release | N/A |
Who Should Use Testo?
Testo is a good fit when standard testing is no longer enough but you still want to stay inside the Go ecosystem.
- Backend teams with large integration suites that need suite-level setup, teardown, and sub-test organization without inventing custom harnesses.
- QA automation engineers who want parameterized scenarios, parallel execution, and reproducible hooks around the same test code.
- Platform and infra teams that need test planning, filtering, reruns, and execution control across many packages.
- Teams building internal test plugins that want to add policy, reporting, or custom methods without forking the framework.
Not ideal for:
- Tiny services with a handful of unit tests where the standard
testingpackage is already enough. - Teams that want BDD-style syntax first and are looking for a Gherkin-centric workflow instead of suite composition.
- Polyglot QA teams that need a single framework across Go, Python, and Java rather than a Go-native abstraction.
Key Features of Testo
- Suite-based test structure — Testo models tests as suites instead of isolated functions, which makes shared setup and teardown much cleaner for long-lived fixtures. The
Suite[T]pattern keeps the framework close totesting.Twhile still giving you a higher-level execution model. - Extensive plugin system — Plugins can add lifecycle hooks, extend
T, override built-in methods likeLogandError, and inject command-line flags. That means you can attach reporting, rerun logic, or policy enforcement without touching individual test cases. - Lifecycle hooks at multiple levels —
BeforeAll,AfterAll,BeforeEach,AfterEach,BeforeSubEach, andAfterSubEachlet you control setup and cleanup at suite, test, and nested sub-test boundaries. This is useful when you need deterministic state management around databases, queues, or external APIs. - Parameterized execution — You can define one test body and run it with many inputs, which reduces duplicate test code and keeps scenario coverage readable. This is especially useful for API matrix testing, table-driven validation, and contract cases.
- Parallel test support — Testo can run tests concurrently so expensive end-to-end suites finish sooner, provided your fixtures are isolated. The framework's execution planning makes it easier to parallelize at the right boundary instead of sprinkling
t.Parallel()everywhere. - Informative errors and traces — Testo emphasizes clear failure output and traceability, which matters when CI logs are the only source of truth. The goal is less guesswork when a nested suite or plugin changes the execution path.
- Caching and reflection — Testo includes key-value caching that survives between runs and reflection over test metadata. That helps when you want to memoize expensive setup or build tools around suite introspection.
Testo vs Alternatives
| Tool | Best For | Key Differentiator | Pricing |
|---|---|---|---|
| Testo | Suite-based Go testing with plugins and hooks | Native testing.T integration plus a plugin architecture for execution planning | Open-Source |
testing package | Simple unit tests and lightweight table tests | Zero extra dependency and first-party Go support | Free |
testify/suite | Small-to-medium suites with familiar assertions | Easier migration path for teams already using Testify assertions | Open-Source |
| Ginkgo v2 | Behavior-driven and highly structured Go test suites | Rich DSL and mature spec runner for opinionated testing workflows | Open-Source |
Pick the standard testing package when you want the least abstraction possible and your tests are mostly table-driven unit cases. Pick testify/suite when you want suite semantics but do not need Testo's plugin planning or hook depth.
Choose Ginkgo v2 when your team prefers a spec DSL and is already invested in BDD-style test organization. Choose Testo when you want suite orchestration, custom plugins, and a thinner layer over testing.T that still keeps your code in idiomatic Go.
If your test workflow is surrounded by shell glue or CI helpers, it is worth pairing Testo with tooling from browse all CLI Tools. For pipelines that coordinate builds, test reruns, and release checks, also look at browse all DevOps Automation tools.
How Testo Works
Testo wraps the standard Go test runtime instead of replacing it, which keeps go test as the execution entry point. The core abstraction is a suite type that embeds testo.Suite[T], where T is a specialized test context built on top of testing.T, and the framework then routes hooks, annotations, and plugin behavior through that context.
That design matters because it keeps most of the behavior explicit and inspectable. Testo does not force you into a separate process model or a custom runner, so the normal Go toolchain, module system, package layout, and IDE support still apply.
Plugins sit in the middle of the execution pipeline. They can add lifecycle callbacks, alter test ordering, duplicate or filter cases, extend the T API, and register go test flags, which makes Testo feel more like a composable execution engine than a thin assertion library.
package main
import (
"testing"
"github.com/ozontech/testo"
)
type T struct { *testo.T }
type Suite struct{ testo.Suite[T] }
func (Suite) TestHelloWorld(t T) {
t.Log("hello from testo!")
}
func Test(t *testing.T) {
testo.RunSuite(t, new(Suite))
}
The example above defines a suite, injects a custom T type, and hands execution to testo.RunSuite. In practice, that means you keep writing ordinary Go tests, but Testo controls the suite lifecycle, plugin dispatch, and any extra behavior you attach through the framework.
Pros and Cons of Testo
Pros:
- Stays inside
testing.Tso you do not need a separate runner or custom binary. - Plugin architecture is first-class and covers hooks, new methods, flags, and execution planning.
- Suite model reduces boilerplate for shared fixtures, database setup, and nested scenarios.
- Parallel execution is built in for teams that can isolate test state.
- Caching between runs is useful for expensive setup and repeated integration scenarios.
- Apache-2.0 license makes it easy to adopt in internal and commercial systems.
Cons:
- More abstraction than plain
testingso tiny projects may not justify the extra layer. - Suite and plugin concepts add cognitive load for developers who only want table-driven tests.
- Go-only focus means it is not a cross-language test standard.
- No visible release metadata in the scraped page so version cadence has to be checked in the repo before a rollout.
- Ecosystem is smaller than
testifyor Ginkgo which can matter if you rely on community examples.
Getting Started with Testo
go get github.com/ozontech/testo
go test .
After those commands, Testo is available as a dependency and your package tests run through the normal Go toolchain. If you want the suite-based style, you will also create a Suite[T] type and call testo.RunSuite from a regular Test function.
For teams adopting Testo in a real repository, the first follow-up is usually wiring one plugin, then one hook, then one parametrized suite. The repository also points to a VS Code extension, which is useful when you need to run or debug individual suite tests without bouncing through terminal-only workflows.
Verdict
Testo is the strongest option for large Go test suites when you need suite-level lifecycle hooks, plugins, and custom execution planning while staying on testing.T. Its biggest strength is composability without framework lock-in, and its main caveat is extra complexity for small projects. If your Go test codebase is growing past plain table tests, Testo is worth adopting.



