Spring Boot Integration
BerryCrush provides seamless integration with Spring Boot, enabling you to:
Run scenarios against a live Spring Boot application
Use dependency injection in bindings classes
Access dynamically allocated ports via
@LocalServerPortLeverage Spring’s test context caching
Setup
Add the Spring module to your dependencies:
Gradle (Kotlin DSL):
dependencies {
testImplementation("org.berrycrush.berrycrush:spring:0.1.0")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
Configuration
Test Class
Annotate your test class with the required annotations:
@Suite
@IncludeEngines("berrycrush")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@BerryCrushContextConfiguration
@BerryCrushScenarios(locations = {"scenarios/*.scenario"})
@BerryCrushConfiguration(bindings = MyBindings.class)
public class MyApiTest {
}
Required Annotations:
Annotation |
Purpose |
|---|---|
|
Starts the embedded Spring Boot application |
|
Enables Spring context integration with BerryCrush |
|
Specifies scenario file locations |
|
Configures bindings, plugins, and step classes |
Bindings Class
Create a Spring-managed bindings class:
@Component
@Lazy // Recommended for port injection timing
public class MyBindings implements BerryCrushBindings {
@LocalServerPort
private int port;
@Override
public Map<String, Object> getBindings() {
return Map.of(
"baseUrl", "http://localhost:" + port + "/api/v1"
);
}
@Override
public String getOpenApiSpec() {
return "my-api.yaml";
}
}
Key Points:
Use
@Componentto make it a Spring beanUse
@Lazyto ensure port is injected before bean creation@LocalServerPortprovides the dynamically allocated portCan inject other Spring beans via
@Autowired
Multi-Spec Support
For APIs with multiple OpenAPI specs:
@Component
@Lazy
public class MyBindings implements BerryCrushBindings {
@LocalServerPort
private int port;
@Override
public Map<String, Object> getBindings() {
return Map.of(
"baseUrl", "http://localhost:" + port + "/api/v1"
);
}
@Override
public String getOpenApiSpec() {
return "petstore.yaml"; // Default spec
}
@Override
public Map<String, String> getAdditionalSpecs() {
return Map.of(
"auth", "auth.yaml", // Authentication APIs
"admin", "admin.yaml" // Admin APIs
);
}
}
Use named specs in scenarios:
scenario: Authenticate and access pets
given I authenticate
call using auth ^login
body: {"username": "test", "password": "test"}
then I have a token
extract $.token => authToken
when I list pets
call ^listPets
header_Authorization: "Bearer {{authToken}}"
then I see pets
assert status 200
Custom Steps with Spring Injection
Create step definitions that use Spring beans:
@Component
public class MySteps {
@Autowired
private UserRepository userRepository;
@Step("a user exists with email {string}")
public void createUser(String email) {
User user = new User();
user.setEmail(email);
userRepository.save(user);
}
@Step("the database should have {int} users")
public void verifyUserCount(int expected) {
long actual = userRepository.count();
assert actual == expected :
"Expected " + expected + " users but found " + actual;
}
}
Register step classes in the configuration:
@BerryCrushConfiguration(
bindings = MyBindings.class,
stepClasses = {MySteps.class}
)
How It Works
The Spring integration uses a BindingsProvider SPI implementation:
Discovery:
SpringBindingsProvideris loaded via ServiceLoaderDetection: Checks for
@BerryCrushContextConfigurationannotationInitialization: Starts Spring ApplicationContext via
SpringContextAdapterBean Retrieval: Gets bindings instance from Spring’s bean container
Cleanup: Releases context after test completion
Architecture
@SpringBootTest
@BerryCrushContextConfiguration
│
▼
SpringBindingsProvider (SPI)
│
▼
SpringContextAdapter
│
├── Starts ApplicationContext
├── Manages lifecycle
└── Retrieves beans
│
▼
BerryCrushBindings instance
(Spring-managed with @Autowired, @LocalServerPort, etc.)
Best Practices
Use @Lazy for bindings: Ensures port injection timing is correct
@Component @Lazy public class MyBindings implements BerryCrushBindings { ... }
Separate concerns: Keep bindings focused on configuration, use step classes for logic
Context caching: Spring automatically caches context across tests with same configuration
Profile support: Use
@ActiveProfilesfor environment-specific configuration@SpringBootTest @ActiveProfiles("test") @BerryCrushContextConfiguration public class MyApiTest { ... }
Database reset: Use
@DirtiesContextif tests modify shared state
Troubleshooting
Port not injected (returns 0)
Ensure bindings class has @Lazy annotation and @SpringBootTest uses RANDOM_PORT.
Bean not found
Verify bindings class has @Component and is in a scanned package.
Context not starting
Check that @SpringBootTest is present alongside @BerryCrushContextConfiguration.
Missing @SpringBootTest annotation
ConfigurationException: Test class 'MyTest' has @BerryCrushContextConfiguration
but is missing @SpringBootTest. Add @SpringBootTest annotation to enable
Spring context integration.