Automated summary
Defines testing standards for Meridian Go services: replacing time.Sleep with the await package, PostgreSQL integration tests via testcontainers, and documented table-driven tests.
Strengths
- Swaps time.Sleep for async assertions with configurable timeout and poll interval, removing a common source of flaky tests
- Enforces per-container isolation through testdb/testhelpers with a mandatory defer Cleanup
- Requires a rationale field on each test case, documenting intent and easing review
- Calls out unhappy-path coverage (defensive testing, ADR-0008) alongside happy paths
Limitations
- Tightly coupled to the Meridian ecosystem (shared/platform/await, testdb, ADR-0008) and not portable as-is
- Assumes Go and testify; no guidance for other languages or test frameworks
- The await package is described without source or details on its polling behavior on failure
Use it when writing or fixing Go tests in a Meridian service, especially for flakiness, async tests, or database integration tests.
Avoid it on non-Meridian or non-Go projects, since the referenced packages and conventions do not exist there.
Security analysis
SafeThe skill contains only static testing guidance and code examples. It declares no tools and does not instruct any destructive, exfiltrating, or obfuscated actions. The content is purely informational for developers.
No concerns found
How this analysis is madeExamples
My TestOrderCompletion in the payments service uses time.Sleep(2 * time.Second) and fails intermittently in CI. Rewrite it using the Meridian await package with a proper timeout.Write a repository integration test for the ledger service that spins up an isolated Postgres container with testhelpers.SetupTestContainer and verifies Create and FindByID.Convert TestDeposit into a table-driven test following Meridian standards, including a rationale field and both happy-path and unhappy-path cases.name: skill-testing-standards description: Testing standards and best practices for Meridian services triggers:
- Writing tests
- Test flakiness or failures
- Using time.Sleep in tests
- Async or polling tests
- Integration tests
- Database tests
- Test coverage
instructions: | Use await package instead of time.Sleep. Use testcontainers for database tests. Apply defensive testing (ADR-0008): test happy paths AND unhappy paths. Table-driven tests with rationale field. Use testify assert/require.
Testing Standards
This document defines testing standards for Meridian services.
Core Principles
- No
time.Sleep- Use theawaitpackage for async assertions - Defensive testing - Test happy paths AND unhappy paths (see ADR-0008)
- Table-driven tests - Use Go table-driven patterns with rationale
- Isolation - Tests must not depend on each other
- Fast feedback - Unit tests should complete in milliseconds
Async Testing: Use await Not time.Sleep
NEVER use time.Sleep in tests. It creates flaky tests - too short causes failures, too long wastes CI time.
Use shared/platform/await instead. It polls until conditions are met or timeout.
import "github.com/meridianhub/meridian/shared/platform/await"
// BAD - arbitrary sleep, flaky and slow
time.Sleep(2 * time.Second)
assert.Equal(t, "COMPLETED", order.Status)
// GOOD - polls until condition met or timeout
err := await.Until(func() bool {
return order.Status == "COMPLETED"
})
require.NoError(t, err)
// With custom timeout and poll interval
err := await.New().
AtMost(5 * time.Second).
PollInterval(50 * time.Millisecond).
Until(func() bool {
return repo.FindByID(ctx, id) != nil
})
// Wait for an operation to succeed (no error)
err := await.UntilNoError(func() error {
return client.HealthCheck()
})
// With context cancellation
err := await.New().
WithContext(ctx).
AtMost(10 * time.Second).
Until(condition)
Defaults: 10s timeout, 100ms poll interval.
For advanced matchers and more expressive assertions, consider gomega.Eventually().
Database Integration Tests: Use Testcontainers
Use shared/platform/testdb/ for PostgreSQL integration tests. Each test gets an isolated container.
import "github.com/meridianhub/meridian/internal/<service>/repository/testhelpers"
func TestRepository(t *testing.T) {
tc := testhelpers.SetupTestContainer(t)
defer tc.Cleanup(t) // CRITICAL - always defer cleanup
// Use tc.Repo for repository operations
err := tc.Repo.Create(ctx, entity)
require.NoError(t, err)
// Or use tc.Pool for direct SQL
var count int
err = tc.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM table").Scan(&count)
require.NoError(t, err)
}
See Testcontainers Usage Guide for detailed patterns.
Table-Driven Tests with Rationale
Every test case must include a rationale field explaining WHY it matters:
func TestDeposit(t *testing.T) {
tests := []struct {
name string
amount int64
wantErr bool
rationale string // WHY this test case matters
}{
// Happy path
{
name: "valid deposit",
amount: 100,
wantErr: false,
rationale: "Standard valid deposit should succeed",
},
// Edge cases
{
name: "minimum valid amount",
amount: 1,
wantErr: false,
rationale: "Even 1 cent is a valid deposit",
},
// Unhappy paths
{
name: "zero amount rejected",
amount: 0,
wantErr: true,
rationale: "Zero deposits are meaningless operations",
},
{
name: "negative amount rejected",
amount: -100,
wantErr: true,
rationale: "Negative deposits don't make sense (use withdraw)",
},
// Defensive: values that shouldn't happen but might
{
name: "overflow detection",
amount: math.MaxInt64,
wantErr: true,
rationale: "Must detect arithmetic overflow to prevent corruption",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := account.Deposit(tt.amount)
if tt.wantErr {
assert.Error(t, err, tt.rationale)
} else {
assert.NoError(t, err, tt.rationale)
}
})
}
}
Defensive Testing Categories
Apply these categories to all tests (see ADR-0008):
For Numeric Inputs
- Valid values (happy path)
- Zero
- Negative values
- Maximum/minimum boundaries
- Overflow conditions
For String Inputs
- Valid formatted strings
- Empty strings
- Whitespace-only strings
- Invalid formats
- Very long strings
For State Transitions
- Valid transitions
- Invalid transitions (must be rejected)
- Idempotent operations
- Concurrent state changes
For Error Handling
- Every error path has a test
- State is not modified on error
- Resources are cleaned up on error
- Errors wrap correctly (
errors.Is/errors.Aswork)
Test Assertions: Use testify
Use github.com/stretchr/testify for assertions:
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Use require for setup - fails fast
tc := testhelpers.SetupTestContainer(t)
require.NotNil(t, tc.Pool, "database pool required")
// Use assert for verifications - continues on failure
assert.Equal(t, expected, actual)
assert.NoError(t, err)
assert.ErrorIs(t, err, ErrNotFound)
assert.Contains(t, logs, "expected message")
require vs assert:
require- Stops test immediately on failure (use for setup/preconditions)assert- Records failure but continues (use for verifications)
Test Naming
Use descriptive names that explain the scenario:
// GOOD - describes scenario and expected outcome
func TestDeposit_ZeroAmount_ReturnsError(t *testing.T)
func TestFindByID_NonExistent_ReturnsNotFoundError(t *testing.T)
func TestTransfer_InsufficientFunds_RollsBackTransaction(t *testing.T)
// BAD - vague names
func TestDeposit1(t *testing.T)
func TestError(t *testing.T)
func TestIt(t *testing.T)
Parallel Tests
Run independent tests in parallel for faster feedback:
func TestConcurrentOperations(t *testing.T) {
t.Parallel() // Mark test as safe for parallel execution
tc := testhelpers.SetupTestContainer(t)
defer tc.Cleanup(t)
// Each parallel test gets its own container
}
When NOT to parallelize:
- Tests sharing global state
- Tests with fixed port bindings
- Tests modifying environment variables
Mocking Guidelines
Minimize mocking. Prefer real implementations where practical:
- Use testcontainers for database tests (real PostgreSQL)
- Use in-memory implementations for simple interfaces
- Mock only external services that can't be containerized
- Never mock the thing you're testing
When mocking is necessary, use github.com/stretchr/testify/mock:
type MockClient struct {
mock.Mock
}
func (m *MockClient) Send(ctx context.Context, msg Message) error {
args := m.Called(ctx, msg)
return args.Error(0)
}
// In test
client := new(MockClient)
client.On("Send", mock.Anything, expectedMsg).Return(nil)
Test Organization
service/
├── domain/
│ ├── account.go
│ └── account_test.go # Unit tests (same package)
├── repository/
│ ├── postgres_repository.go
│ ├── postgres_repository_test.go # Integration tests
│ └── testhelpers/
│ └── container.go # Shared test setup
└── handler/
├── grpc_handler.go
└── grpc_handler_test.go # Handler tests
CI Considerations
- Unit tests run on every commit
- Integration tests (testcontainers) run on PR
- Tests must pass before merge
- Flaky tests must be fixed immediately (not skipped)
Related Documentation
- ADR-0008: Defensive Testing Standards - Testing philosophy
- Testcontainers Usage Guide - Database test setup
- await package - Async polling utility
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/testing-standards-for-meridian-services-mrx3xztb/download' --output '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md.part' && mv '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md.part' '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.
Claude Code
test -f '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md' && test -s '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/skill-testing-standards' && cp -R '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb' '.claude/skills/skill-testing-standards'Cursor
test -f '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md' && test -s '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/skill-testing-standards' && cp -R '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb' '.cursor/skills/skill-testing-standards'Codex
test -f '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md' && test -s '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md' && mkdir -p '.agents/skills' && test ! -e '.agents/skills/skill-testing-standards' && cp -R '.skill-downloads/testing-standards-for-meridian-services-mrx3xztb' '.agents/skills/skill-testing-standards'OpenSkills
OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.
Official OpenSkills installationopenskills install './.skill-downloads/testing-standards-for-meridian-services-mrx3xztb'openskills read 'testing-standards-for-meridian-services-mrx3xztb'Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.
For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.
openskills syncManual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/testing-standards-for-meridian-services-mrx3xztb/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
ClickHouse Performance Comparison
Testing
Evaluate ClickHouse performance test results, check PR performance changes, compare with history, and assess whether a result is actionable.
Analyze CI Performance Reports
Testing
Analyze CI performance comparison reports for a ClickHouse PR. Cross-references master history to distinguish real regressions from flaky tests.
Santa Method
Testing
Multi-agent adversarial verification with two independent reviewers. Output is checked twice and iteratively fixed until both pass before shipping.