Architecture¶
System Overview¶
OpenAPI Test Generator is a Kotlin/JVM toolchain that turns OpenAPI specs into executable tests or test-suite artifacts. It targets:
- CLI users who want a fast generator for local runs or CI
- Gradle users who want generation wired into build pipelines
- Integrators who embed the core engine to customize rules, data providers, or generators
The design centers on a small, deterministic core that owns parsing, rule application, and test-suite assembly, with optional feature modules (template generation, pattern-based values) injected explicitly by CLI/Gradle to avoid reflection-based discovery.
What This Tool Tests¶
OpenAPI Test Generator validates infrastructure-level behavior - the contract compliance layer between API consumers and your controllers. This is distinct from business logic testing.
Infrastructure validation (what we test)¶
- Parameter validation: Type checking, format constraints (date, email, uuid), min/max bounds, enum membership, required vs optional
- Request body validation: JSON schema compliance, nested object structure, array limits, property constraints
- Security enforcement: Authentication presence, valid credentials format, correct security scheme usage
Business logic (what we don't test)¶
- Data semantics (e.g., "order total must equal sum of line items")
- Authorization rules beyond API-level security (e.g., "users can only access their own orders")
- Side effects (e.g., "creating an order should send a confirmation email")
- State transitions (e.g., "orders can only be cancelled before shipping")
The generated tests verify that your API rejects invalid requests with appropriate error codes (400, 401, 403) - they don't verify what happens when valid requests are processed.
Related docs¶
- Documentation index: docs home
- Getting started: Getting started
- How-to guides: How-to
- Development: development setup
- Core module: core
- Reference:
Module Dependency Graph¶
Layered Architecture¶
flowchart TB
subgraph Entry["Entry Points"]
CLI["CLI<br/>(native + JVM)"]
Plugin["Gradle Plugin<br/>(OpenApiTestGeneratorTask)"]
end
subgraph Dist["Distribution Bundle"]
Runner["TestGenerationRunner<br/>(orchestration, reporter, default wiring)"]
end
subgraph Features["Feature Modules"]
PatternSupport["pattern-support<br/>(regex rule + module wiring)"]
Template["generator-template<br/>(Mustache codegen)"]
end
subgraph Core["Core"]
Providers["Providers<br/>(param, body, auth)"]
Rules["Rules<br/>(schema, auth)"]
Engine["TestGenerationEngine<br/>(orchestration, config)"]
end
subgraph Values["Value Generation"]
PatternValue["pattern-value<br/>(regex value generator)"]
ExampleValue["example-value<br/>(value providers, schema merger)"]
end
subgraph Model["Model"]
Types["TestCase, TestSuite, Outcome,<br/>GenerationReport, ErrorMode"]
end
CLI --> Runner
Plugin --> Runner
Runner --> Template
Runner --> PatternSupport
PatternSupport --> Core
Template --> Core
PatternSupport --> PatternValue
Core --> ExampleValue
PatternValue --> ExampleValue
ExampleValue --> Model Value Provider Dependency Chain¶
The value generation stack follows a strict dependency direction:
flowchart LR
PV["pattern-value"] --> EV["example-value"] --> M["model"] pattern-value contains:
PatternValueProvider(implements SchemaValueProvider SPI)PatternValueGenerator(regexp-gen wrapper)- No core dependency
example-value contains:
SchemaValueProviderSPI interface- Built-in providers (enum, date, uuid, etc.)
SchemaMerger,CartesianProduct
This layering enables standalone use of pattern-value for regex-based string generation without pulling in the test generation framework.
Module Responsibilities¶
| Module | Responsibility | Key Types |
|---|---|---|
| build-logic | Convention plugins for centralized build config | testgen.kotlin-base, testgen.quality, testgen.library |
| model | Data classes, error types | TestCase, TestSuite, Outcome, GenerationError |
| example-value | Schema value generation SPI + builtins | SchemaValueProvider, SchemaExampleValueGenerator, SchemaMerger |
| core | Parsing, generation, orchestration | TestGenerationEngine, providers, rules, TestSuiteWriter |
| pattern-value | Regex-based value generation | PatternGenerationOptions, PatternValueGenerator, PatternValueProvider |
| pattern-support | Pattern integration module | PatternSupportModule, PatternModuleSettingsExtractor |
| generator-template | Mustache-based code generation | TemplateGeneratorModule, TemplateArtifactGenerator |
| distribution-bundle | Bundles modules, execution wiring | TestGenerationRunner, TestGenerationReporter, DistributionDefaults |
| plugin | Gradle build integration | OpenApiTestGeneratorPlugin, OpenApiTestGeneratorTask |
| cli | Command-line interface + native | GenerateCommand, Picocli wiring |
Data Flow¶
- Entry point: CLI or Gradle creates a
TestGenerationRunner(viawithDefaults()or builder) with an environment-specificTestGenerationReporter. - Configuration resolution:
TestGenerationRunner.execute()callsTestGeneratorExecutionOptionsFactory.fromConfigto merge overrides over YAML config and apply defaults. - Parse OpenAPI:
OpenApiSpecParser.parseOpenApiloads and resolves the OpenAPI document withParseOptions().apply { isResolveFully = true }. - Build generation pipeline:
TestGenerationEngine.createProcessorwires schema merging, value providers, rules, and providers viaTestGeneratorConfigurer. - Generate suites per operation:
ValidCaseBuilderconstructs a baseline valid test case from operation parameters, request body, and security requirements.TestGenerationContextwraps the valid case, OpenAPI model, and helper services (data providers, schema merger).ProviderOrchestratorexecutes providers in fixed order (auth -> parameters -> request body).- Each provider applies rules to generate negative test cases expecting 400/401/403 status codes.
OutcomeAggregatorcollects provider results, merging test cases and errors.TestCaseBudgetValidatorenforces per-operation limits on generated test cases.
- Report outcomes:
GenerationReportcaptures successes, partials, and failures withGenerationErrormetadata.TestGenerationRunnerformats and logs the report via its reporter. - Emit artifacts:
TestGenerationEngine.createArtifactGeneratorbuilds a generator by id (test-suite-writerortemplate) and writes files. - Return result:
TestGenerationRunnerreturnsTestGenerationResult.SuccessorTestGenerationResult.Failurefor CLI/Gradle to handle appropriately.
Core Abstractions¶
Provider-Rule Architecture Pattern¶
- Providers implement
TestCaseProvider<T>and operate on OpenAPI elements (operations, parameters, request bodies). They are orchestrated in a fixed order byProviderOrchestrator. - Rules encode constraints:
SchemaValidationRuleproducesRuleValueentries that providers turn into test cases.AuthValidationRuleproduces completeTestCaseobjects for security scenarios.
- Composition for array/object schemas is handled through
ArrayItemSchemaValidationRuleandObjectItemSchemaValidationRuleusing aRuleContainer. - Registry:
ManualRuleRegistrywiresBuiltInRulesplus any extra rules, ensuring deterministic ordering.
Generator Extensibility Model¶
- Artifact generators are created through
ArtifactGeneratorFactoryandArtifactGeneratorRegistryusing generator ids (GeneratorIds). - Built-in generators are registered explicitly by
BuiltInGenerators(currentlytest-suite-writer). - Feature modules implement
TestGenerationModuleto contribute:ArtifactGeneratorFactoryimplementationsSchemaValueProviderinstances (by id)- Additional
SimpleSchemaValidationRuleandAuthValidationRulerules
- CLI and Gradle pass modules explicitly (e.g.,
TemplateGeneratorModule,PatternSupportModule) to enable optional capabilities.
Configuration Resolution Chain¶
- Inputs:
- YAML config (
GeneratorConfig) loaded byGeneratorConfigLoader. - Environment overrides from CLI args or Gradle DSL (
TestGeneratorOverrides).
- YAML config (
- Merge:
TestGeneratorExecutionOptionsFactorymerges overrides over config (overrides win).- Module-specific settings are extracted via
ModuleSettingsExtractorbefore core settings parsing. TestGenerationSettingsandExampleValueSettingssupply defaults when values are missing.
- Defaults:
- CLI and Gradle insert the
patternexample-value provider into the default provider order beforeplain-string.
- CLI and Gradle insert the
- Note: The merge is deep for nested maps in
generatorOptionsandtestGenerationSettings. Collections are replaced (not merged), and map/non-map mismatches prefer the override value (no fail-fast).
Test Suite Generation Pipeline¶
This is an architecture-level summary of how the generator turns an OpenAPI operation into a TestSuite and artifacts. For the canonical, step-by-step breakdown, see Test generation flow. See also Budget controls and the TestCase reference for output field semantics.
ValidCaseBuilder¶
Builds the baseline valid TestCase for each operation, used as the seed for generating negative cases. See Test generation flow.
Response Example Resolution¶
Resolves expected responses from explicit OpenAPI examples, with deterministic fallbacks when examples are missing. See TestCase reference and Module: example-value.
TestGenerationContext¶
Carries the OpenAPI model, operation, valid case, and shared helpers (schema merger, example value generator, budgets) through generation. See Test generation flow.
Provider Execution Order¶
Runs providers in a fixed order (auth → parameters → request body) and aggregates outcomes into a TestSuite. See Test generation flow and Provider-rule model.
Budget Controls¶
Budgets cap schema depth/combinations and maximum test cases per operation to keep generation tractable. See Budget controls.
Schema Processing¶
SchemaMerger¶
Handles OpenAPI schema composition (allOf, anyOf, oneOf):
- Flattens composed schemas into a single merged schema for validation.
- Preserves constraint semantics (e.g., tightest bounds win for min/max).
- Tracks recursion depth via
SchemaMergerOptions.maxMergedSchemaDepth. - Works with
CombinationBudgetto limit cartesian explosion.
Cycle Detection¶
SchemaStructureHashercomputes structural hashes to detect schema cycles.TestGenerationContextimplementations trackvisitedSchemaRefsandvisitedStructuresto prevent infinite loops.- Cycle detection is deterministic and does not rely on object identity.
Example Value Generation¶
Providers are tried in configured order until one produces a value:
- schema-example: Uses OpenAPI
exampleorexamplesfrom the schema. - pattern (pattern-support module): Generates values matching regex patterns.
- plain-string: Falls back to basic type-appropriate values.
ExampleValueSettings.providers controls the order. SchemaExampleValueGeneratorFactory wires the configured providers into a SchemaExampleValueGenerator.
Module Deep-Dives¶
Per-module responsibilities and entry points are documented under Modules. Start there for the authoritative "what lives where" view, then jump into the module page for the area you're working on.
Common starting points:
Cross-Cutting Concerns¶
Ignore Configuration¶
Test case and rule filtering is controlled via TestGenerationSettings:
- ignoreTestCases: Map of exact paths to operation/test case filters. Supports wildcard path (
*) and wildcard method (*); test case names are exact matches. - ignoreSchemaValidationRules: Set of rule names to skip (e.g.,
"OutOfMinimumLengthString"). - ignoreAuthValidationRules: Set of auth rule names to skip.
IgnoreConfigHandler applies filters during generation:
- Path matching: Filters operations by exact path or wildcard path.
- Method matching: Filters by HTTP method within matched paths (case-insensitive; wildcard supported).
- Test case matching: Filters individual test cases by exact name.
Filtering is applied deterministically after test case generation to ensure stable output.
GraalVM / Native Image Compatibility¶
- CLI uses the GraalVM Native Build Tools plugin (
:cli:nativeCompile). - Rule and generator wiring is explicit (
BuiltInRules,BuiltInGenerators,TestGenerationModule), avoiding reflection for discovery. - Reflection still exists in specific areas: the template generator uses Mustache's
ReflectionObjectHandler, and the Gradle plugin uses Kotlin reflection to copy settings. - The OpenAPI parser and Jackson can require reflection configuration for native images (see CLI documentation for agent-based config generation).
Determinism Guarantees¶
- Rules and generators are registered and sorted deterministically (
BuiltInRules,ManualRuleRegistry,BuiltInGenerators). TestGenerationEnginevalidates and sorts modules by id to ensure stable ordering.ExampleValueSettings.providersdefines provider precedence; missing providers are logged, and fallback ordering is deterministic.TestSuiteWritersorts suite keys and test cases during merge to stabilize output.
Error Handling Philosophy¶
- Errors as values: Most domain errors are captured as values, not thrown exceptions.
- Outcome type hierarchy:
Outcome.Success<T>: Operation completed with result.Outcome.PartialSuccess<T>: Operation produced results but also encountered errors.Outcome.Failure: Operation failed entirely with error list.
- Error aggregation:
OutcomeAggregatormerges provider results, accumulating test cases and errors. FinalOutcometype depends on whether any test cases were produced. - Error boundary:
runProviderSafelywraps provider execution, converting uncaught exceptions toOutcome.Failurewith meaningful context. - Error modes:
ErrorHandlingConfigcontrols behavior:ErrorMode.FAIL_FAST: Stop on first error.ErrorMode.COLLECT_ALL: Accumulate errors up tomaxErrorslimit (default 100).
- Budget violations:
BudgetExceededExceptionfrom provider logic (schema combinations/depth) is converted toGenerationErrorat provider boundaries.TestCaseBudgetValidatorexceptions currently propagate unless caught by the caller. - Error context: Every
GenerationErrorincludesErrorContextwith operation path, method, operationId, and optionally field/rule name for precise error location.