π Modern, extensible, and composable Go test framework.
Made with β€οΈ by @NikitaFilonov
- β¨ About
- π¦ Installation
- π Quick Start
- β Why Axiom?
- π Documentation
Axiom is a modern testing framework for Go, built around extensibility, composition, and a clean, powerful
runtime execution model. It enhances Goβs standard testing package with capabilities normally found in mature
ecosystems like pytest, JUnit5, and allure frameworks β without hiding or replacing Goβs native tooling.
Axiom provides:
- Composable test configuration β merge global & local config seamlessly (
RunnerβCase). - Powerful runtime engine β deterministic lifecycle, step execution, subtests, and retries.
- Hooks system β before/after test, step, and subtest execution.
- Plugins API β extend framework behavior without touching the core.
- Fixtures β lazy-evaluated resources with automatic cleanup.
- Metadata system β tags, severity, labels, epics, features, stories.
- Parallelization control β opt-in at both runner & case granularity.
Axiom is not a DSL replacement β itβs a composable execution engine that sits on top of Goβs native testing stack and supercharges it.
go get github.com/Nikita-Filonov/axiomFor version pinning:
go get github.com/Nikita-Filonov/axiom@v0.3.0This example demonstrates the core power of Axiom: fixtures, metadata, hooks, plugins, steps, and retryable subtests β all working together seamlessly.
package example_test
import (
"fmt"
"testing"
"github.com/Nikita-Filonov/axiom"
"github.com/Nikita-Filonov/axiom/plugins/testallure"
"github.com/Nikita-Filonov/axiom/plugins/teststats"
"github.com/Nikita-Filonov/axiom/plugins/testtags"
)
// -----------------------------------------------------------------------------
// Fixtures
// -----------------------------------------------------------------------------
// DBFixture simulates a database connection with automatic teardown.
func DBFixture(cfg *axiom.Config) (any, func(), error) {
// setup
db := fmt.Sprintf("db-connection-%s", cfg.ID)
// teardown
cleanup := func() {
fmt.Printf("Closing %s\n", db)
}
return db, cleanup, nil
}
// UserFixture depends on the DB fixture and derives a user from it.
func UserFixture(cfg *axiom.Config) (any, func(), error) {
db := axiom.GetFixture[string](cfg, "db")
user := fmt.Sprintf("user-from-%s", db)
return user, nil, nil
}
// -----------------------------------------------------------------------------
// Global Runner (shared test environment)
// -----------------------------------------------------------------------------
var runner = axiom.NewRunner(
axiom.WithRunnerMeta(
axiom.WithMetaEpic("authentication"),
axiom.WithMetaFeature("login"),
axiom.WithMetaSeverity(axiom.SeverityCritical),
),
// Plugins extend the runtime behavior:
axiom.WithRunnerPlugins(
testtags.Plugin(testtags.WithConfigInclude("smoke")), // filter by tag
teststats.Plugin(teststats.NewStats()), // metrics
testallure.Plugin(), // reporting
),
// Global retry configuration (per test case):
axiom.WithRunnerRetry(
axiom.WithRetryTimes(3),
axiom.WithRetryDelay(15),
),
// Global fixtures:
axiom.WithRunnerFixture("db", DBFixture),
// Enable parallel execution across test cases:
axiom.WithRunnerParallel(),
)
func TestUserLogin(t *testing.T) {
c := axiom.NewCase(
axiom.WithCaseName("user can login with valid credentials"),
axiom.WithCaseMeta(
axiom.WithMetaTag("smoke"),
axiom.WithMetaStory("valid login"),
axiom.WithMetaLabel("component", "auth-service"),
),
// Local fixtures:
axiom.WithCaseFixture("user", UserFixture),
)
runner.RunCase(t, c, func(cfg *axiom.Config) {
cfg.Step("prepare user", func() {
user := axiom.GetFixture[string](cfg, "user")
fmt.Println("Using:", user)
})
cfg.Step("validate response", func() {
fmt.Println("Login OK")
})
})
}Goβs built-in testing package is intentionally minimal. This philosophy makes tests simple, fast, and approachable β but it also leaves developers on their own when building large, structured, and maintainable test suites.
When a project grows, you quickly run into hard limitations:
- no fixtures or resource lifecycle management
- no before/after hooks
- no retry mechanism for flaky operations
- no metadata (tags, severity, labels, layers, epics)
- no step model for readable reporting
- no plugin or extension architecture
- no way to compose configuration (global β per-test)
- limited reporting capabilities
- and no clear path to build these features on top
Most teams end up reinventing these tools internally β often in incompatible ways.
Instead of replacing Goβs testing ecosystem, Axiom extends it with a powerful execution engine:
- Fixtures β lazy-evaluated, cached resources with automatic cleanup
- Hooks β before/after test, step, and subtest
- Retries β deterministic flaky-test handling
- Metadata β tags, severity, labels, epics, features, stories
- Step model β structured execution with reporting support
- Plugins β clean extension mechanism for integrating tooling (Allure, metrics, filtering, etc.)
- Composable configuration β merge global runner settings with per-test overrides
- Parallelization control β opt-in, explicit, predictable
Axiom preserves the core spirit of Go β clarity, composability, explicit behavior β while adding the missing building blocks needed for serious test engineering.
Itβs not a DSL replacement. Itβs not a βmagicβ wrapper.
Axiom is a test runtime engine that unlocks capabilities traditionally found in frameworks like pytest, JUnit5, and Allure, but implemented the Go way: simple, explicit, and pragmatic.
Axiom includes structured, minimal, and maintainable documentation for every core concept of the framework. See the following folders:
- ./docs/usage β realistic end-to-end example of building a test framework with Axiom
- ./docs/philosophy β design principles and how Axiom fits into the Go testing ecosystem
- ./docs/runner β global execution environment, plugins, hooks, shared fixtures, retries
- ./docs/case β declarative test definitions, metadata, parameters, per-test configuration
- ./docs/config β merged runtime state for each test attempt (steps, wraps, hooks, fixtures, metadata)
- ./docs/runtime β execution runtime: wraps, logs, artefacts, sinks
- ./docs/fixture β lazy resource lifecycle, fixture dependencies, automatic cleanup
- ./docs/resource β runner-scoped shared resources, lifecycle, concurrency, deterministic teardown
- ./docs/meta β metadata: tags, labels, severity, epics, features, stories, layers
- ./docs/log β structured logging via Runtime log sinks
- ./docs/assert β structured assertion events and runtime assert sinks
- ./docs/artefacts β binary and structured test outputs
- ./docs/parallel β parallel execution flags and merging behavior
- ./docs/retry β retry policies, isolated attempts, override rules
- ./docs/skip β static & dynamic skip rules with reasons
- ./docs/hooks β lifecycle hooks for tests, steps, and subtests
- ./docs/params β typed parameter injection for test cases
- ./docs/context β structured global and per-test context values
- ./docs/plugins β plugin system, built-in plugins, and guidelines for writing custom plugins
- ./docs/glossary β definitions of all core Axiom concepts
