Skip to content

Commit 2639601

Browse files
authored
Merge pull request #45 from rekby/work/enhance-project-with-readme-and-documentation
Streamline README quick start with bundled context sample
2 parents 03ac35e + a95d880 commit 2639601

11 files changed

Lines changed: 679 additions & 185 deletions

README.md

Lines changed: 164 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,186 @@
11
[![Go Reference](https://pkg.go.dev/badge/github.com/rekby/fixenv.svg)](https://pkg.go.dev/github.com/rekby/fixenv)
22
[![Coverage Status](https://coveralls.io/repos/github/rekby/fixenv/badge.svg?branch=master)](https://coveralls.io/github/rekby/fixenv?branch=master)
33
[![GoReportCard](https://goreportcard.com/badge/github.com/rekby/fixenv)](https://goreportcard.com/report/github.com/rekby/fixenv)
4-
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
4+
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
55

6-
Go Fixtures
7-
===========
6+
# Fixenv
87

9-
Inspired by pytest fixtures. No dependencies.
8+
Pytest-inspired fixture caching for Go tests with precise scope control, automatic cleanup, and no external dependencies.
109

11-
[Examples](https://github.com/rekby/fixenv/tree/master/examples)
10+
Fixenv helps Go developers describe repeatable test environments once and reuse them safely across an entire test suite. Fixtures are cached per configurable scope, dependencies between fixtures are tracked automatically, and cleanup hooks make resource lifecycle management explicit.
1211

13-
The package provide engine for write and use own fixtures.
12+
---
1413

15-
Fixture - function-helper for provide some object/service for test.
16-
Fixture calls with same parameters cached and many time calls of the fixture return same result
17-
and work did once only.
14+
- [Features](#features)
15+
- [Installation](#installation)
16+
- [Quick start](#quick-start)
17+
- [Fixture scopes & caching](#fixture-scopes--caching)
18+
- [Cleanup & ordering guarantees](#cleanup--ordering-guarantees)
19+
- [Documentation](#documentation)
20+
- [Example gallery](#example-gallery)
21+
- [Comparison with alternatives](#comparison-with-alternatives)
22+
- [Project roadmap](#project-roadmap)
23+
- [Contributing](#contributing)
24+
- [License](#license)
1825

19-
```golang
20-
package example
26+
---
2127

22-
// counter fixture - increment globalCounter every non cached call
23-
// and return new globalCounter value
24-
// cache shared one test
25-
func counter(e fixenv.Env) int {...}
28+
## Features
2629

27-
func TestCounter(t *testing.T) {
28-
e := fixenv.NewEnv(t)
29-
30-
r1 := counter(e)
31-
r2 := counter(e)
32-
if r1 != r2 {
33-
t.Error()
34-
}
35-
36-
t.Run("subtest", func(t *testing.T) {
37-
e := fixenv.NewEnv(t)
38-
r3 := counter(e)
39-
if r3 == r1 {
40-
t.Error()
41-
}
42-
})
43-
}
30+
- **Scope-aware caching** – Run expensive setup only once per test, test tree, or package using [`CacheOptions.Scope`](https://pkg.go.dev/github.com/rekby/fixenv#CacheOptions).
31+
- **Deterministic cleanup** – Register cleanup callbacks via [`NewGenericResultWithCleanup`](https://pkg.go.dev/github.com/rekby/fixenv#NewGenericResultWithCleanup); Fixenv invokes them when the owning scope ends.
32+
- **Fixture dependency tree** – Compose fixtures freely; cached results are shared across the dependency tree to avoid duplicated work.
33+
- **Skip-aware execution** – Return [`ErrSkipTest`](https://pkg.go.dev/github.com/rekby/fixenv#pkg-variables) from fixtures to short-circuit tests without leaking resources.
34+
- **Extensible environment** – Embed [`EnvT`](https://pkg.go.dev/github.com/rekby/fixenv#EnvT) or implement the [`Env`](https://pkg.go.dev/github.com/rekby/fixenv#Env) interface to customise behaviour for your project.
35+
- **Parallel-ready fixtures** – Caching and cleanup stay correct when tests call [`t.Parallel`](https://pkg.go.dev/testing#T.Parallel); each parallel test receives its own safe environment.
36+
- **Zero dependencies** – Fixenv is a lightweight helper that plays well with the standard `testing` package.
37+
- **Starter fixtures included** – Import [`github.com/rekby/fixenv/sf`](sf) for ready-to-use helpers like cancellable contexts, temporary directories, and TCP listeners.
38+
39+
## Installation
40+
41+
```bash
42+
go get github.com/rekby/fixenv
4443
```
4544

45+
Fixenv follows Go modules semantic import versioning. Re-run the command above to upgrade to the latest tag.
4646

47-
For example with default scope test - it work will done once per test.
48-
With scope TestAndSubtests - cache shared by test (TestFunction(t *testing.T)) and all of that subtest.
49-
With package scope - result cached for all test in package.
50-
Fixture can have cleanup function, that called while out of scope.
51-
52-
Fixture can call other fixtures, cache shared between them.
53-
54-
For example simple account test:
55-
```golang
56-
package example
57-
58-
// db create database abd db struct, cached per package - call
59-
// once and same db shared with all tests
60-
func db(e Env)*DB{...}
61-
62-
// DbCustomer - create customer with random personal data
63-
// but fixed name. Fixture result shared by test and subtests,
64-
// then mean many calls Customer with same name will return same
65-
// customer object.
66-
// Call Customer with other name will create new customer
67-
// and resurn other object.
68-
func DbCustomer(e Env, name string) Customer {
69-
// ... create customer
70-
db(e).CustomerStore(cust)
71-
// ...
72-
return cust
73-
}
47+
## Quick start
48+
49+
### Use bundled fixtures instantly
7450

75-
// DbAccount create bank account for customer with given name.
76-
func DbAccount(e Env, customerName, accountName string)Account{
77-
cust := DbCustomer(e, customerName)
78-
// ... create account
79-
db(e).AccountStore(acc)
80-
// ...
81-
return acc
51+
```go
52+
package context_test
53+
54+
import (
55+
"testing"
56+
57+
"github.com/rekby/fixenv"
58+
"github.com/rekby/fixenv/sf"
59+
)
60+
61+
func TestAutoCanceledContext(t *testing.T) {
62+
e := fixenv.New(t)
63+
ctx := sf.Context(e)
64+
65+
// Exercise your code under test with ctx.
66+
// When the test finishes, the context is cancelled automatically.
8267
}
68+
```
69+
70+
`sf.Context` ships with Fixenv and returns a cancellable context bound to the current test scope. When the test ends, the fixture automatically cancels the context through its registered cleanup—no manual teardown required.
8371

84-
func TestFirstOwnAccounts(t *testing.T){
85-
e := NewEnv(t)
86-
// background:
87-
// create database
88-
// create customer bob
89-
// create account from
90-
accFrom := DbAccount(e, "bob", "from")
91-
92-
// get existed db, get existed bob, create account to
93-
accTo := DbAccount(e, "bob", "to")
94-
95-
PutMoney(accFrom, 100)
96-
SendMoney(accFrom, accTo, 20)
97-
if accFrom != 80 {
98-
t.Error()
99-
}
100-
if accTo != 20 {
101-
t.Error()
102-
}
103-
104-
// background:
105-
// delete account to
106-
// delete account from
107-
// delete customer bob
72+
### Write a custom fixture
73+
74+
```go
75+
package counter_test
76+
77+
import (
78+
"testing"
79+
80+
"github.com/rekby/fixenv"
81+
)
82+
83+
var globalCounter int
84+
85+
func counter(e fixenv.Env) int {
86+
return fixenv.CacheResult(e, func() (*fixenv.GenericResult[int], error) {
87+
globalCounter++
88+
return fixenv.NewGenericResult(globalCounter), nil
89+
})
10890
}
10991

110-
func TestSecondTransferBetweenCustomers(t *testing.T){
111-
e := NewEnv(t)
112-
113-
// background:
114-
// get db, existed from prev test
115-
// create customer bob
116-
// create account main for bob
117-
accFrom := DbAccount(e, "bob", "main")
118-
119-
// background:
120-
// get existed db
121-
// create customer alice
122-
// create account main for alice
123-
accTo := DbAccount(e, "alice", "main")
124-
PutMoney(accFrom, 100)
125-
SendMoney(accFrom, accTo, 20)
126-
if accFrom != 80 {
127-
t.Error()
128-
}
129-
if accTo != 20 {
130-
t.Error()
131-
}
132-
133-
// background:
134-
// remove account of alice
135-
// remove customer alice
136-
// remove account of bob
137-
// remove customer bob
92+
func TestCounter(t *testing.T) {
93+
e := fixenv.New(t)
94+
if counter(e) != counter(e) {
95+
t.Fatal("value must be cached within a single test scope")
96+
}
13897
}
98+
```
99+
100+
This pattern—create an environment with `fixenv.New(t)`, wrap setup logic in `fixenv.CacheResult`, and optionally attach a cleanup—is the basis for all fixtures. Continue with the [Getting started guide](docs/getting-started.md) for step-by-step instructions and cleanup examples.
101+
102+
## Fixture scopes & caching
103+
104+
Fixtures default to `ScopeTest`: cached per `testing.T`. Subtests get their own scope unless you opt in to sharing. Override the scope through `CacheOptions` to share expensive setup more broadly:
105+
106+
| Scope | Lifetime | Typical use cases |
107+
|-------|----------|-------------------|
108+
| `ScopeTest` | Current `testing.T` only | Pure unit tests, short-lived resources |
109+
| `ScopeTestAndSubtests` | Top-level test and all nested subtests | Reusing setup across a parent test and its subtests |
110+
| `ScopePackage` | Entire package (requires `TestMain`) | Shared databases, external services, heavy caches |
111+
112+
Scope names combine automatically with the fixture's call site to produce a stable cache key. You can extend the cache key with serialisable parameters via `CacheOptions.CacheKey` when a fixture accepts arguments.
139113

140-
// background:
141-
// after all test finished drop database
114+
Learn more in [Scopes and lifetimes](docs/scopes-and-lifetimes.md).
115+
116+
## Cleanup & ordering guarantees
117+
118+
Each fixture may optionally return a cleanup callback. Fixenv registers the callback on the owning `testing.T` and guarantees **LIFO** execution when the scope ends. Cleanups run even if a test fails or is skipped via `ErrSkipTest`, making it safe to provision external services, create temporary files, or adjust global state.
119+
120+
See [Cleanup and ordering](docs/cleanup-and-ordering.md) for practical recipes.
121+
122+
## Documentation
123+
124+
The `docs/` directory provides extended guides:
125+
126+
- [Getting started](docs/getting-started.md) – installation, basic fixtures, and first tests.
127+
- [Scopes and lifetimes](docs/scopes-and-lifetimes.md) – choosing the right cache level and structuring packages.
128+
- [Advanced fixtures](docs/advanced-fixtures.md) – parameterised fixtures, dependency injection patterns, and custom environments.
129+
- [Cleanup and ordering](docs/cleanup-and-ordering.md) – teardown techniques, deterministic ordering, and debugging tips.
130+
- [Example walkthroughs](docs/examples/README.md) – narrative tours of the sample projects.
131+
132+
## Example gallery
133+
134+
Explore runnable examples under [`examples/`](examples):
135+
136+
| Example | Highlights |
137+
| ------- | ---------- |
138+
| [`simple`](examples/simple) | Minimal fixtures, scope defaults, and cleanup basics. |
139+
| [`custom_env`](examples/custom_env) | Embedding `EnvT` in a domain-specific helper to expose project-specific fixtures. |
140+
| [`sf_helpers`](examples/sf_helpers) | Using bundled fixtures from `sf` for contexts, temp directories, and local TCP listeners. |
141+
| [`simple_main_test`](examples/simple_main_test) | Using `TestMain` to install package-level fixtures with cleanup control. |
142+
143+
Run the full set with:
144+
145+
```bash
146+
go test ./examples/...
142147
```
148+
149+
Detailed walkthroughs and expected outputs live in [docs/examples/README.md](docs/examples/README.md).
150+
151+
## Comparison with alternatives
152+
153+
| Feature / Project | Fixenv | `testify/suite` | `dockertest` | `gotest.tools/fs` |
154+
| -------------------------------- | :----: | :-------------: | :----------: | :---------------: |
155+
| Scoped caching (test / package) || ⚪️ manual | ⚪️ manual | ⚪️ manual |
156+
| Declarative fixture tree || ⚪️ | ⚪️ | ⚪️ |
157+
| Cleanup integration | ✅ (LIFO with fixtures) | ⚪️ (`testing.T.Cleanup`) | ✅ (containers) | ⚪️ (`testing.T.Cleanup`) |
158+
| Skip-aware setup | ✅ (`ErrSkipTest`) | ⚪️ | ⚪️ | ⚪️ |
159+
| Works with plain `testing.T` |||||
160+
| Extra runtime requirements | Go stdlib only | Go stdlib only | Docker daemon | Go stdlib only |
161+
162+
Fixenv focuses on composing reusable fixtures with deterministic lifecycle control. It complements assertion libraries and environment provisioning tools—you can mix Fixenv with them instead of choosing only one approach.
163+
164+
## Project roadmap
165+
166+
- Additional built-in helpers for temporary directories and HTTP servers.
167+
- Optional tracing hooks for fixture execution and cache hits.
168+
- Community-contributed examples covering databases, message queues, and cloud resources.
169+
- Automatic detection of scope-mixing mistakes (e.g. invoking a test-scoped fixture from a package-scoped one) to surface lifecycle issues early.
170+
171+
Interested in a feature? [Open an issue](https://github.com/rekby/fixenv/issues/new) or start a discussion.
172+
173+
## Contributing
174+
175+
Contributions are welcome! Please:
176+
177+
1. Fork the repository and create a feature branch.
178+
2. Include thorough automated tests with each code change.
179+
3. Run `go test ./...` before opening a pull request.
180+
4. Describe your changes in detail and link to related issues.
181+
182+
Bug reports and documentation improvements are also appreciated.
183+
184+
## License
185+
186+
Licensed under the [MIT License](LICENSE.txt).

docs/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Fixenv documentation
2+
3+
Welcome to the extended Fixenv guides. Start with [Getting started](getting-started.md) for installation and the first fixture, then dive into specific topics:
4+
5+
- [Scopes and lifetimes](scopes-and-lifetimes.md)
6+
- [Advanced fixtures](advanced-fixtures.md)
7+
- [Cleanup and ordering](cleanup-and-ordering.md)
8+
- [Example walkthroughs](examples/README.md)
9+
10+
These documents complement the [project README](../README.md) and the [Go reference](https://pkg.go.dev/github.com/rekby/fixenv).

0 commit comments

Comments
 (0)