Chronicle is a comprehensive testing framework for Golang that enables robust integration testing, infrastructure management, and continuous scenario-based testing.
- Infrastructure Management: Provision and manage test infrastructure using real containers
- Scenario-Based Testing: Build and compose test scenarios using a fluent builder pattern
- Continuous Testing: Run scenarios continuously with randomized inputs for chaos testing
- Extensible Architecture: Easily extend with custom providers, components, and generators
- Suite & Test Separation: Clear separation between suite-level and test-level constructs
- Type-Safe Identifiers: Strongly-typed identifiers for tests, services, and components
go get github.com/joshua-temple/chronicle
import (
"testing"
"github.com/joshua-temple/chronicle/core"
"github.com/joshua-temple/chronicle/infrastructure"
"github.com/joshua-temple/chronicle/runner"
)
func TestExample(t *testing.T) {
// Define infrastructure requirements
redisService := infrastructure.NewServiceRequirement("redis", "redis:latest")
infrastructure.AddPort(&redisService, "default", 6379)
// Create infrastructure provider
provider := infrastructure.NewTestContainersProvider()
provider.AddService(redisService)
// Run test with infrastructure
runner.RunTest(t, func(ctx context.Context, tc *core.TestContext) error {
// Your test code here
return nil
}, runner.WithInfrastructureProvider(provider))
}
The core
package provides the foundation of the framework with types and interfaces that define test contexts, infrastructure providers, services, and more.
Chronicle supports strongly-typed identifiers for tests, services, components, and more:
// Define typed identifiers
const (
// Service IDs
RedisService core.ServiceID = "infrastructure.redis"
// Test IDs
UserCreationTest core.TestID = "user.creation"
// Component IDs
SetupDatabaseComp core.ComponentID = "components.setup.database"
)
// Use typed identifiers
registry := core.NewIDRegistry()
registry.RegisterServiceID(RedisService)
registry.RegisterTestID(UserCreationTest)
registry.RegisterComponentID(SetupDatabaseComp)
Chronicle supports multiple infrastructure providers:
The TestContainers provider integrates with the testcontainers-go library to provide real container-based infrastructure for tests.
// Create TestContainers provider
provider := infrastructure.NewTestContainersProvider()
// Define and add service requirements
redisService := infrastructure.NewServiceRequirement("redis", "redis:latest")
infrastructure.AddPort(&redisService, "default", 6379)
provider.AddService(redisService)
// Initialize and start infrastructure
provider.Initialize(ctx)
provider.Start(ctx)
For more complex infrastructure needs, Chronicle provides a Docker Compose provider:
// Create Docker Compose provider
provider := infrastructure.NewDockerComposeProvider("./docker-compose.yml", "my-project")
The Scenarios framework allows for composition of test components into reusable scenarios:
import (
"github.com/joshua-temple/chronicle/scenarios"
)
// Define components
setupDatabase := func(ctx *scenarios.Context) error {
ctx.Info("Setting up database...")
return nil
}
createUser := func(ctx *scenarios.Context) error {
username, _ := ctx.GetParameter("username")
ctx.Info("Creating user: %v", username)
return nil
}
// Build scenario
scenario := scenarios.NewBuilder("User Management Test").
WithDescription("Tests user creation and validation").
WithTimeout(30*time.Second).
SetupWith(setupDatabase).
AddComponent("CreateUser", createUser).
WithStringParameter(
"username",
"Username for testing",
5, 10,
"abcdefghijklmnopqrstuvwxyz",
).
Build()
// Execute the scenario
ctx := scenarios.NewContext()
err := scenario.Execute(ctx)
The Suite framework provides a way to manage groups of related tests with shared infrastructure:
import (
"github.com/joshua-temple/chronicle/suite"
"github.com/joshua-temple/chronicle/core"
)
// Create a suite
testSuite := suite.NewSuite(core.SuiteID("user-suite"))
testSuite.WithDescription("User Management Suite")
testSuite.WithInfraProvider(provider)
testSuite.WithGlobalSetup(setupDatabase)
testSuite.WithGlobalTeardown(cleanupDatabase)
// Register components with the suite
testSuite.RegisterComponent(core.ComponentID("setup"), setupDatabase)
testSuite.RegisterComponent(core.ComponentID("create"), createUser)
// Create tests
userCreationTest := suite.NewTest(core.TestID("user.create"))
userCreationTest.WithComponents(core.ComponentID("create"))
userCreationTest.WithStateAccess(suite.ReadWrite)
// Add tests to the suite
testSuite.AddTests(userCreationTest)
// Run the suite
err := testSuite.Run(context.Background())
The Daemon service provides continuous testing capabilities with enhanced configuration:
import (
"github.com/joshua-temple/chronicle/daemon"
)
// Create enhanced daemon configuration
config := daemon.NewEnhancedDaemonConfig()
config.MaxConcurrency = 5
config.GlobalThrottleRate = 30 // Max 30 test starts per minute
config.ShutdownGracePeriod = 30 * time.Second
// Configure time windows
userTestConfig := &daemon.TestExecutionConfig{
Frequency: 5 * time.Minute,
MaxParallelSelf: 1,
Jitter: 30 * time.Second,
TimeWindows: []daemon.TimeWindow{
{
DaysOfWeek: []time.Weekday{time.Monday, time.Friday},
StartTime: "09:00",
EndTime: "17:00",
TimeZone: "America/New_York",
},
},
}
// Add test config
config.TestConfigs[core.TestID("user.creation")] = userTestConfig
// Create and start the scheduler
store := daemon.NewTestScenarioStore()
logger := daemon.NewDefaultLogger()
scheduler := daemon.NewExecutionScheduler(config, store, logger)
scheduler.Start()
Chronicle provides built-in generators for test parameters:
StringGenerator
: Generates random strings with configurable length and character setIntGenerator
: Generates random integers within a specified rangeEnumGenerator
: Selects random values from a predefined set of options
Custom generators can be created by implementing the Generator
interface.
Components can be conditionally executed based on runtime conditions:
// Add conditional component
scenario.WithConditionalComponent(
"PaymentProcessor",
func(ctx *scenarios.Context) bool {
paymentType, _ := ctx.GetParameter("paymentType")
return paymentType == "credit_card"
},
processCreditCardPayment,
)
The daemon service supports customizable result reporting:
// Create custom output handler
type MyOutputHandler struct{}
func (h *MyOutputHandler) HandleResult(result *daemon.ScenarioResult) {
// Process and store/display results
}
func (h *MyOutputHandler) Flush() {
// Ensure all results are processed
}
// Add to daemon config
config.OutputHandlers = []daemon.OutputHandler{
&MyOutputHandler{},
}
Chronicle clearly separates suite-level and test-level concerns:
- Suite: Manages infrastructure, coordinates tests, and provides shared state
- Test: Focuses on specific functionality with clear dependencies
Benefits:
- Improved organization of related tests
- Centralized management of infrastructure
- Coordinated setup and teardown
- Dependency-based test ordering
- Controlled state sharing between tests
The enhanced daemon configuration provides:
- Time Windows: Run tests only during specific days and times
- Jitter: Add randomness to scheduling to prevent resource contention
- Resource Limits: Control CPU, memory, and disk usage
- Quota Management: Limit test execution frequency
- Quarantine: Automatically disable tests after consecutive failures
- Chaos Testing: Introduce controlled chaos to test resilience
See the examples/
directory for complete usage examples, including:
examples/simple_test.go
- Basic usageexamples/scenario_test.go
- Scenario-based testingexamples/enhanced_test.go
- Suite pattern and enhanced daemon
This project is licensed under the terms specified in the LICENSE file.