Class Hierarchy
This document provides a comprehensive view of the key classes and interfaces in BerryCrush.
Core Module (berrycrush/core)
Domain Model
org.berrycrush.model
├── Scenario # Test scenario with steps
├── Step # Single step in a scenario
├── StepType # Enum: GIVEN, WHEN, THEN, AND, BUT
├── Fragment # Reusable step collection
├── ExampleRow # Parameter row for scenario outline
├── Extraction # Value extraction definition
├── Assertion # Assertion definition
├── AssertionType # Enum: STATUS_CODE, JSON_PATH, SCHEMA, etc.
├── ScenarioResult # Execution result for a scenario
├── StepResult # Execution result for a step
├── AssertionResult # Result of an assertion
├── ResultStatus # Enum: PASSED, FAILED, SKIPPED, ERROR
├── ValidationError # Schema validation error
└── FragmentRegistry # Registry for fragments by name
DSL Classes
org.berrycrush.dsl
├── @BerryCrushDsl # DSL marker annotation
├── BerryCrushSuite # Main entry point for DSL
│ ├── spec(path) # Register OpenAPI spec
│ ├── configure { } # Configure execution
│ ├── scenario(name) { } # Define scenario
│ ├── scenarioOutline(name) { } # Define parameterized scenario
│ └── fragment(name) { } # Define fragment
├── ScenarioScope # DSL scope for scenarios
│ ├── given(description) { }
│ ├── when(description) { }
│ ├── then(description) { }
│ ├── and(description) { }
│ ├── but(description) { }
│ └── include(fragment)
├── ScenarioOutlineScope # DSL scope for parameterized scenarios
│ ├── (same as ScenarioScope)
│ └── examples { }
├── StepScope # DSL scope for step definition
│ ├── using(specName)
│ ├── call(operationId) { }
│ ├── extractTo(var, jsonPath)
│ ├── statusCode(expected)
│ ├── bodyEquals(path, value)
│ └── bodyArrayNotEmpty(path)
├── CallScope # DSL scope for API calls
│ ├── pathParam(name, value)
│ ├── queryParam(name, value)
│ ├── header(name, value)
│ ├── body(content)
│ ├── bearerToken(token)
│ ├── basicAuth(user, pass)
│ └── apiKey(key)
└── FragmentScope # DSL scope for fragments
Scenario Parsing
org.berrycrush.scenario
├── Lexer # Tokenizes scenario files
│ └── tokenize(source): List<Token>
├── Parser # Parses tokens into AST
│ └── parse(source): ParseResult
├── ScenarioLoader # Loads scenarios from files/strings
│ ├── loadScenariosFromFile(path)
│ ├── loadScenariosFromString(source)
│ ├── loadFileContent(path) # Returns ScenarioFileContent
│ └── loadFragmentsFromFile(path)
├── ScenarioFileContent # Parsed file with scenarios and parameters
├── Token # Lexer token
├── TokenType # Token type enum
└── AST Node Classes
├── ScenarioNode
├── StepNode
├── FragmentNode
└── ParametersNode
Execution Engine
org.berrycrush.executor
├── ScenarioExecutor # Main executor
│ ├── execute(scenario, context)
│ └── execute(scenario, sharedContext, sourceFile)
├── HttpRequestBuilder # Builds HTTP requests
│ ├── build(operation, params)
│ └── substituteVariables(template, context)
└── ResponseHandler # Processes HTTP responses
├── handleResponse(response, step)
└── runAssertions(response, assertions)
OpenAPI Integration
org.berrycrush.openapi
├── SpecRegistry # Manages OpenAPI specs
│ ├── registerDefault(path, config)
│ ├── register(name, path, config)
│ ├── getDefault(): OpenAPI?
│ └── get(name): OpenAPI?
├── OperationResolver # Resolves operations
│ ├── resolve(operationId): ResolvedOperation?
│ └── resolve(specName, operationId): ResolvedOperation?
├── OpenApiLoader # Loads OpenAPI specs
│ └── load(path): OpenAPI
└── ResolvedOperation # Resolved operation data
├── method: HttpMethod
├── path: String
├── parameters: List<Parameter>
└── requestBody: RequestBody?
Step Definitions
org.berrycrush.step
├── @Step # Annotation for step methods
│ ├── pattern: String # Step pattern with placeholders
│ └── description: String # Optional description
├── StepDefinition # Represents a step definition
│ ├── pattern: String
│ ├── method: Method
│ ├── instance: Any?
│ └── description: String
├── StepRegistry # Interface for step lookup
│ ├── register(definition)
│ ├── registerAll(definitions)
│ └── find(stepText): StepMatch?
├── DefaultStepRegistry # Default implementation
├── StepMatcher # Matches step text to patterns
│ └── match(pattern, text): MatchResult?
├── AnnotationStepScanner # Scans classes for @Step
│ ├── scan(clazz): List<StepDefinition>
│ └── scanAll(vararg classes)
├── PackageStepScanner # Scans packages for steps
│ └── scan(packageName): List<StepDefinition>
└── StepDsl # DSL for step definitions
└── step(pattern) { ... }
Plugin System
org.berrycrush.plugin
├── BerryCrushPlugin # Plugin interface
│ ├── id: String
│ ├── name: String
│ ├── priority: Int
│ ├── onTestExecutionStart()
│ ├── onTestExecutionEnd()
│ ├── onScenarioStart(context)
│ ├── onScenarioEnd(context, result)
│ ├── onStepStart(context)
│ └── onStepEnd(context, result)
├── PluginRegistry # Manages plugins
│ ├── register(plugin)
│ ├── register(pluginClass)
│ ├── registerByName(name)
│ ├── replace(plugin)
│ ├── dispatchTestExecutionStart()
│ ├── dispatchTestExecutionEnd()
│ ├── dispatchScenarioStart(context)
│ ├── dispatchScenarioEnd(context, result)
│ ├── dispatchStepStart(context)
│ └── dispatchStepEnd(context, result)
├── PluginNameResolver # Resolves plugin names
│ └── resolve(name): BerryCrushPlugin
├── ScenarioContext # Context for scenario lifecycle
├── StepContext # Context for step lifecycle
├── ScenarioResult # Plugin-facing result
└── StepResult # Plugin-facing step result
Reporting
org.berrycrush.report
├── ReportPlugin (abstract) # Base class for report plugins
│ ├── outputPath: Path
│ ├── generateReport()
│ ├── buildReport(): TestReport
│ └── formatReport(report): String # Abstract
├── TextReportPlugin # Human-readable text format
├── JsonReportPlugin # JSON format
├── XmlReportPlugin # XML format
├── JunitReportPlugin # JUnit XML format (CI/CD)
├── TestReport # Report data model
├── ScenarioReportEntry # Scenario in report
├── StepReportEntry # Step in report
└── TestSummaryBuilder # Builds test summary
Configuration
org.berrycrush.config
├── Configuration # Main configuration
│ ├── baseUrl: String?
│ ├── timeout: Duration
│ ├── defaultHeaders: Map
│ ├── environment: String?
│ ├── autoAssertions: AutoAssertionConfig
│ ├── strictSchemaValidation: Boolean
│ ├── followRedirects: Boolean
│ ├── logRequests: Boolean
│ ├── logResponses: Boolean
│ ├── httpLogger: HttpLogger?
│ ├── logFormatter: HttpLogFormatter?
│ └── shareVariablesAcrossScenarios: Boolean
├── AutoAssertionConfig # Auto-assertion settings
│ ├── enabled: Boolean
│ ├── statusCode: Boolean
│ ├── contentType: Boolean
│ └── schema: Boolean
└── SpecConfiguration # Per-spec configuration
├── baseUrl: String?
└── headers: Map
Context and Variables
org.berrycrush.context
├── ExecutionContext # Runtime variable storage
│ ├── get(name): Any?
│ ├── set(name, value)
│ ├── createChild(): ExecutionContext
│ ├── allVariables(): Map
│ └── substitute(template): String
└── ValueExtractor # Extracts values from responses
├── extractJsonPath(json, path): Any?
└── extractHeader(response, name): String?
Logging
org.berrycrush.logging
├── HttpLogger # Interface for HTTP logging
│ ├── logRequest(method, url, headers, body)
│ └── logResponse(method, url, response, duration)
├── HttpLogFormatter # Interface for formatting
│ ├── formatRequest(method, url, headers, body)
│ └── formatResponse(method, url, response, duration)
├── ConsoleHttpLogger # Console output logger
├── JulHttpLogger # java.util.logging logger
└── HttpLoggerFactory # Creates default loggers
Exceptions
org.berrycrush.exception
├── BerryCrushException # Base exception
├── ConfigurationException # Configuration errors
├── ScenarioParseException # Parsing errors
├── StepExecutionException # Step execution errors
└── AssertionException # Assertion failures
JUnit Module (berrycrush/junit)
Annotations
org.berrycrush.junit
├── @BerryCrushScenarios # Specify scenario locations
│ ├── locations: Array<String>
│ └── fragments: Array<String>
├── @BerryCrushConfiguration # Configure execution
│ ├── bindings: KClass
│ ├── timeout: Long
│ ├── plugins: Array<String>
│ ├── pluginClasses: Array<KClass>
│ ├── stepClasses: Array<KClass>
│ └── stepPackages: Array<String>
├── @BerryCrushSpec # Specify OpenAPI specs
│ ├── paths: Array<String>
│ ├── baseUrl: String
│ └── name: String
├── @BerryCrushTags # Filter by tags
│ ├── include: Array<String>
│ └── exclude: Array<String>
└── @BerryCrushTimeout # Scenario timeout
├── value: Long
└── unit: TimeUnit
Bindings
org.berrycrush.junit
├── BerryCrushBindings # Interface for runtime bindings
│ ├── getBindings(): Map<String, Any>
│ ├── configure(config)
│ ├── getPlugins(): List<BerryCrushPlugin>
│ └── getStepClasses(): Array<Class<*>>
├── DefaultBindings # No-op implementation
└── ScenarioTest (abstract) # Base class for DSL tests
├── configureSuite() # Override to configure
└── defineScenarios() # Override to define scenarios
Configuration Values
org.berrycrush.config
└── OpenApiSpecValue # OpenAPI spec configuration
├── location: String
└── baseUrl: String?
Test Engine
org.berrycrush.junit.engine
├── BerryCrushTestEngine # JUnit 5 TestEngine
│ ├── getId(): String # "berrycrush"
│ ├── discover(request, id): TestDescriptor
│ └── execute(request)
├── BerryCrushEngineDescriptor # Root test descriptor
├── ClassTestDescriptor # Per-class descriptor
├── ScenarioFileDescriptor # Per-file descriptor
├── ScenarioTestDescriptor # Per-scenario descriptor
└── TestDescriptors # Descriptor utilities
Discovery
org.berrycrush.junit.discovery
├── ScenarioDiscovery # Discovers scenario files
│ └── discoverScenarios(classLoader, patterns)
├── FragmentDiscovery # Discovers fragment files
│ └── discoverFragments(classLoader, patterns)
└── ResourceDiscovery # General resource discovery
└── discoverResources(classLoader, patterns)
SPI
org.berrycrush.junit.spi
└── BindingsProvider # SPI for custom bindings creation
├── supports(testClass): Boolean
├── priority(): Int
├── initialize(testClass)
├── createBindings(testClass, bindingsClass)
└── cleanup(testClass)
Spring Module (berrycrush/spring)
org.berrycrush.spring
├── @BerryCrushContextConfiguration # Spring integration annotation
├── SpringBindingsProvider # BindingsProvider for Spring
│ ├── supports(testClass): Boolean
│ ├── priority(): Int # 100 (high priority)
│ ├── initialize(testClass)
│ ├── createBindings(testClass, bindingsClass)
│ └── cleanup(testClass)
├── SpringContextAdapter # Spring ApplicationContext bridge
│ ├── initializeContext()
│ ├── getBean<T>(type): T
│ └── cleanup()
└── SpringStepDiscovery # Auto-discover steps from beans
└── discoverSteps(context): List<StepDefinition>
Class Relationships
Scenario Execution Flow
BerryCrushTestEngine
│
├── uses ──▶ ScenarioDiscovery
│ │
│ └── returns ──▶ Scenario files
│
├── uses ──▶ ScenarioLoader
│ │
│ └── returns ──▶ List<Scenario>
│
├── creates ──▶ ScenarioExecutor
│ │
│ ├── uses ──▶ SpecRegistry
│ ├── uses ──▶ Configuration
│ ├── uses ──▶ PluginRegistry
│ └── uses ──▶ StepRegistry
│
└── reports ──▶ JUnit Platform
Plugin Hierarchy
BerryCrushPlugin (interface)
│
├── ReportPlugin (abstract)
│ │
│ ├── TextReportPlugin
│ ├── JsonReportPlugin
│ ├── XmlReportPlugin
│ └── JunitReportPlugin
│
└── Custom plugins...
Bindings Provider Chain
ServiceLoader<BindingsProvider>
│
├── SpringBindingsProvider (priority: 100)
│ │
│ └── If @BerryCrushContextConfiguration present
│
└── DefaultBindingsProvider (priority: 0)
│
└── Reflection-based instantiation
Key Extension Points
1. Custom Plugins
Implement BerryCrushPlugin:
class MyPlugin : BerryCrushPlugin {
override val name = "my-plugin"
override val priority = 50
override fun onScenarioStart(context: ScenarioContext) {
// Custom logic
}
}
2. Custom Step Definitions
Use @Step annotation:
class MySteps {
@Step("I have {int} items")
fun setItemCount(count: Int) {
// Custom logic
}
}
3. Custom Bindings Provider
Implement BindingsProvider SPI:
class MyBindingsProvider : BindingsProvider {
override fun supports(testClass: Class<*>) = true
override fun priority() = 50
override fun createBindings(testClass, bindingsClass) = ...
}
4. Custom HTTP Logger
Implement HttpLogger:
class MyLogger : HttpLogger {
override fun logRequest(method, url, headers, body) {
// Custom logging
}
}