Skip to content

0.20.0 (To be released)

Release Date: To be released

This release introduces:

  • Simplified Module Names: All modules renamed to remove testing-e2e suffix for cleaner artifact names
  • New BOM (Bill of Materials): Centralized version management via stove-bom
  • Package Structure Simplification: Package names simplified from com.trendyol.stove.testing.e2e.* to com.trendyol.stove.*
  • Test Reporting System: Comprehensive reporting that tracks all actions and assertions during test execution
  • Spring Boot 4.x Support: Full support for Spring Boot 4.x and Spring Kafka 4.x
  • Unified Spring Modules: Single modules that work across Spring Boot 2.x, 3.x, and 4.x
  • New Bean Registration DSL: stoveSpring4xRegistrar for Spring Boot 4.x (since BeanDefinitionDsl is deprecated)
  • Runtime Version Checks: Clear error messages when Spring Boot/Kafka is missing from classpath
  • Ktor DI Flexibility: Support for Koin, Ktor-DI, or custom resolvers
  • Generic Type Resolution: using<List<T>> now works correctly with full generic type preservation

New Features

Test Reporting System

Stove now includes a built-in reporting system that captures everything that happens during your tests. When a test fails, you get a detailed report showing exactly what happened, making debugging much easier.

Key capabilities:

  • Automatic tracking of all system interactions (HTTP, Kafka, database, WireMock, gRPC)
  • Test failure enrichment with detailed execution reports embedded in test output
  • System snapshots showing internal state (Kafka messages, WireMock stubs) at the time of failure
  • Multiple renderers - human-readable console output or machine-readable JSON
  • Framework integration with both Kotest and JUnit
  • Stack trace preservation - original stack traces are preserved in test failures

Quick Start - Kotest

Add the extension dependency (optional but recommended):

dependencies {
    testImplementation("com.trendyol:stove-extensions-kotest")
}

Then configure:

import com.trendyol.stove.extensions.kotest.StoveKotestExtension
import com.trendyol.stove.system.Stove

class TestConfig : AbstractProjectConfig() {
    override val extensions: List<Extension> = listOf(StoveKotestExtension())

    override suspend fun beforeProject() {
        Stove()
            .with { /* your configuration */ }
            .run()
    }

    override suspend fun afterProject() {
        Stove.stop()
    }
}

Quick Start - JUnit

Add the extension dependency (optional but recommended):

dependencies {
    testImplementation("com.trendyol:stove-extensions-junit")
}

Then configure:

import com.trendyol.stove.extensions.junit.StoveJUnitExtension
import org.junit.jupiter.api.extension.ExtendWith

@ExtendWith(StoveJUnitExtension::class)
class MyE2ETest {
    // your tests
}

The JUnit extension works with both JUnit 5 and 6 since they share the Jupiter API.

Configuration

Stove {
    reporting {
        enabled()           // Enable reporting (default: true)
        dumpOnFailure()     // Dump report when tests fail (default: true)
        failureRenderer(PrettyConsoleRenderer)  // Set the renderer
    }
}

Example Output

When a test fails, you'll see output like:

╔══════════════════════════════════════════════════════════════════════════════╗
║                           STOVE TEST EXECUTION REPORT                        ║
║ Test: should save the product                                                ║
║ Status: FAILED                                                               ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ 14:47:38.215 ✓ PASSED [HTTP] POST /api/products                              ║
║     Input: {"id":1234,"name":"Test Product"}                                 ║
║                                                                              ║
║ 14:47:38.341 ✗ FAILED [Kafka] shouldBePublished<ProductCreatedEvent>         ║
║     Expected: Message matching condition within 5s                           ║
║     Actual: No matching message found                                        ║
║     Error: GOT A TIMEOUT: While expecting the publish of 'ProductCreatedEvent'║
╠══════════════════════════════════════════════════════════════════════════════╣
║ SYSTEM SNAPSHOTS                                                             ║
║ ┌─ KAFKA ────────────────────────────────────────────────────────────────────║
║   Consumed: 0                                                                ║
║   Produced: 1                                                                ║
║   State Details:                                                             ║
║     produced: 1 item(s)                                                      ║
║       [0] topic: product-events, value: {"id":1234,"name":"Test Product"}    ║
╚══════════════════════════════════════════════════════════════════════════════╝

Available Renderers

  • PrettyConsoleRenderer (default) - Colorized, box-drawing output for terminals
  • JsonReportRenderer - Machine-readable JSON for CI/CD integration

See the Reporting documentation for full details.


Spring Boot 4.x Support

Stove now fully supports Spring Boot 4.x and Spring Kafka 4.x. The existing stove-spring and stove-spring-kafka modules work with all Spring Boot versions (2.x, 3.x, and 4.x).

Dependencies remain the same:

testImplementation("com.trendyol:stove-spring:0.20.0")
testImplementation("com.trendyol:stove-spring-kafka:0.20.0")

New Bean Registration DSL for Spring Boot 4.x

Spring Boot 4.x deprecates BeanDefinitionDsl (beans { } DSL). Stove provides new extension functions for cleaner bean registration:

Spring Boot 2.x / 3.x - use addTestDependencies:

import com.trendyol.stove.addTestDependencies

springBoot(
    runner = { params ->
        runApplication<MyApp>(args = params) {
            addTestDependencies {
                bean<TestSystemKafkaInterceptor<*, *>>()
                bean<MyService> { MyServiceImpl() }
            }
        }
    }
)

Spring Boot 4.x - use addTestDependencies4x:

import com.trendyol.stove.addTestDependencies4x

springBoot(
    runner = { params ->
        runApplication<MyApp>(args = params) {
            addTestDependencies4x {
                registerBean<TestSystemKafkaInterceptor<*, *>>(primary = true)
                registerBean<MyService> { MyServiceImpl() }
            }
        }
    }
)

Alternative: Using addInitializers directly:

// Spring Boot 2.x / 3.x
addInitializers(stoveSpringRegistrar { bean<MyService>() })

// Spring Boot 4.x
addInitializers(stoveSpring4xRegistrar { registerBean<MyService>() })

Key differences for 4.x: - Use registerBean<T>() instead of bean<T>() - Use registerBean<T>(primary = true) for primary beans - No ref() function - use constructor injection instead


Ktor DI Flexibility

Stove's Ktor module now supports multiple dependency injection systems. Previously, Koin was required. Now you can use:

  1. Koin (existing support)
  2. Ktor-DI (new built-in support)
  3. Custom resolver (any DI framework)

Both Koin and Ktor-DI are now compileOnly dependencies - you bring your preferred DI system.

Using Koin:

dependencies {
    testImplementation("io.insert-koin:koin-ktor:$koinVersion")
}

// In your test setup
bridge() // Auto-detects Koin

Using Ktor-DI:

dependencies {
    testImplementation("io.ktor:ktor-server-di:$ktorVersion")
}

// In your test setup
bridge() // Auto-detects Ktor-DI

Using a Custom Resolver:

For any other DI framework (Kodein, Dagger, manual, etc.):

bridge { application, type ->
    // Your custom resolution logic - type is KType preserving generics
    myDiContainer.resolve(type)
}

Generic Type Resolution in Bridge System

The using<T> function now properly preserves generic type information, allowing you to resolve types like List<PaymentService>:

// Register multiple implementations
provide<List<PaymentService>> {
    listOf(StripePaymentService(), PayPalPaymentService())
}

// Resolve with full generic type preserved
validate {
    using<List<PaymentService>> {
        forEach { service -> service.pay(order) }
    }
}

This works by using KType instead of KClass internally, which preserves generic type parameters that would otherwise be lost due to JVM type erasure.

For custom BridgeSystem implementations: Override getByType(type: KType) to support generic types. The default implementation falls back to get(klass: KClass).


Ktor Test Dependency Registration

Unlike Spring Boot's unified addTestDependencies, Ktor test dependency registration differs by DI framework:

Koin:

// In your app - accept test modules
fun run(args: Array<String>, testModules: List<Module> = emptyList()): Application {
    return embeddedServer(Netty, port = args.getPort()) {
        install(Koin) { modules(appModule, *testModules.toTypedArray()) }
    }.start(wait = false).application
}

// In tests - pass test modules with overrides
ktor(runner = { params ->
    MyApp.run(params, testModules = listOf(
        module {
            single<TimeProvider>(override = true) { FixedTimeProvider() }
        }
    ))
})

Ktor-DI:

// In your app - accept test dependencies
fun run(args: Array<String>, testDeps: (DependencyRegistrar.() -> Unit)? = null): Application {
    return embeddedServer(Netty, port = args.getPort()) {
        install(DI) {
            dependencies {
                provide<MyService> { MyServiceImpl() }
                testDeps?.invoke(this)  // Later provides override earlier ones
            }
        }
    }.start(wait = false).application
}

// In tests - pass test overrides
ktor(runner = { params ->
    MyApp.run(params) {
        provide<TimeProvider> { FixedTimeProvider() }
    }
})

Runtime Version Checks

When Spring Boot, Spring Kafka, or Ktor DI is missing from the classpath, Stove now provides clear error messages:

═══════════════════════════════════════════════════════════════════════════════
  Spring Boot Not Found on Classpath!
═══════════════════════════════════════════════════════════════════════════════

  stove-spring requires Spring Boot to be on your classpath.
  Spring Boot is declared as a 'compileOnly' dependency, so you must add it
  to your project.

  Add one of the following to your build.gradle.kts:

  For Spring Boot 2.x:
    testImplementation("org.springframework.boot:spring-boot-starter:2.7.x")

  For Spring Boot 3.x:
    testImplementation("org.springframework.boot:spring-boot-starter:3.x.x")

  For Spring Boot 4.x:
    testImplementation("org.springframework.boot:spring-boot-starter:4.x.x")

═══════════════════════════════════════════════════════════════════════════════

Migration Guide

From 0.19.x to 0.20.0

This is a breaking change release. Follow these steps to migrate:

1. Update Module Names (Required)

See the Breaking Changes - Module and Package Renaming section above for detailed migration steps and regex patterns.

Quick Summary: - Update all artifact names in build files - Replace stove-testing-e2estove - Replace stove-*-testing-e2estove-* - Update all package imports from com.trendyol.stove.testing.e2e.*com.trendyol.stove.*

2. Test Framework Extensions (Optional)

Test framework extensions are now in separate modules. They're optional but recommended for better failure reporting. Add the one that matches your test framework:

dependencies {
    // For Kotest
    testImplementation("com.trendyol:stove-extensions-kotest")

    // OR for JUnit 5/6
    testImplementation("com.trendyol:stove-extensions-junit")
}

Update your imports:

// Kotest
import com.trendyol.stove.extensions.kotest.StoveKotestExtension

// JUnit
import com.trendyol.stove.extensions.junit.StoveJUnitExtension

The new BOM simplifies version management:

dependencies {
    testImplementation(platform("com.trendyol:stove-bom:0.20.0"))

    // No versions needed - managed by BOM
    testImplementation("com.trendyol:stove")
    testImplementation("com.trendyol:stove-spring")
    testImplementation("com.trendyol:stove-kafka")
}

From 0.19.x (Other Changes)

Test Extensions for Better Reporting

The reporting extensions are optional but make debugging much easier. Add the one for your test framework:

Kotest:

// Add dependency: testImplementation("com.trendyol:stove-extensions-kotest")
class TestConfig : AbstractProjectConfig() {
    override val extensions = listOf(StoveKotestExtension())
}

JUnit:

@ExtendWith(StoveJUnitExtension::class)
class MyE2ETest { }

For Spring Boot 2.x and 3.x Users

If using BaseApplicationContextInitializer: Migrate to addTestDependencies (see Breaking Changes below).

If using beans { } directly: Your existing code continues to work. Optionally, use the new cleaner API:

// Old way (still works)
addInitializers(beans { bean<MyService>() })

// New way (recommended)
addTestDependencies { bean<MyService>() }

For Spring Boot 4.x Users (New!)

Spring Boot 4.x is newly supported in this release. Use addTestDependencies4x:

import com.trendyol.stove.addTestDependencies4x

springBoot(
    runner = { params ->
        runApplication<MyApp>(args = params) {
            addTestDependencies4x {
                registerBean<TestSystemKafkaInterceptor<*, *>>(primary = true)
                registerBean<MyService> { MyServiceImpl() }
            }
        }
    }
)

Note: The beans { } DSL from Spring is deprecated in 4.x, which is why Stove provides addTestDependencies4x with registerBean<T>().


Breaking Changes

Module and Package Renaming

⚠️ BREAKING: All Stove modules have been renamed to simplify artifact names and package structure. This is a breaking change that requires updates to your build files and source code.

Module Name Changes

Old Artifact Name New Artifact Name
stove-testing-e2e stove
stove-testing-e2e-kafka stove-kafka
stove-testing-e2e-http stove-http
stove-testing-e2e-couchbase stove-couchbase
stove-testing-e2e-elasticsearch stove-elasticsearch
stove-testing-e2e-grpc stove-grpc
stove-testing-e2e-mongodb stove-mongodb
stove-testing-e2e-redis stove-redis
stove-testing-e2e-wiremock stove-wiremock
stove-testing-e2e-rdbms-postgres stove-postgres
stove-testing-e2e-rdbms-mssql stove-mssql
stove-spring-testing-e2e stove-spring
stove-spring-testing-e2e-kafka stove-spring-kafka
stove-ktor-testing-e2e stove-ktor
stove-micronaut-testing-e2e stove-micronaut

Package Name Changes

All packages have been simplified: - com.trendyol.stove.testing.e2e.*com.trendyol.stove.* - com.trendyol.stove.testing.e2e.rdbms.postgrescom.trendyol.stove.postgres - com.trendyol.stove.testing.e2e.rdbms.mssqlcom.trendyol.stove.mssql - com.trendyol.stove.testing.e2e.standalone.kafkacom.trendyol.stove.kafka

Migration Guide

Step 1: Update Build Files (Gradle/Maven)

Recommended: Use the new BOM for version management:

// build.gradle.kts
dependencies {
    // Import BOM
    testImplementation(platform("com.trendyol:stove-bom:$version"))

    // Core and framework (no version needed - managed by BOM)
    testImplementation("com.trendyol:stove")
    testImplementation("com.trendyol:stove-spring")  // or stove-ktor, stove-micronaut

    // Components (no version needed)
    testImplementation("com.trendyol:stove-kafka")
    testImplementation("com.trendyol:stove-postgres")
    // ... other components
}

Or without BOM (specify versions explicitly):

dependencies {
    testImplementation("com.trendyol:stove:$version")
    testImplementation("com.trendyol:stove-spring:$version")
    testImplementation("com.trendyol:stove-kafka:$version")
    testImplementation("com.trendyol:stove-postgres:$version")
}

Step 2: Update Package Imports in Source Code

All import statements need to be updated. Use the regex patterns below for automated migration.

Step 3: Automated Migration with Regex

Use these regex patterns in your IDE's find-and-replace (with regex enabled):

For Build Files (Gradle/Maven):

  1. Replace artifact names in dependencies:

    Find:    com\.trendyol:stove-testing-e2e(?!-)
    Replace: com.trendyol:stove
    

  2. Replace component artifacts:

    Find:    com\.trendyol:stove-testing-e2e-([a-z-]+)
    Replace: com.trendyol:stove-$1
    

  3. Replace RDBMS artifacts:

    Find:    com\.trendyol:stove-testing-e2e-rdbms-(postgres|mssql)
    Replace: com.trendyol:stove-$1
    

  4. Replace starter artifacts:

    Find:    com\.trendyol:stove-(spring|ktor|micronaut)-testing-e2e(-kafka)?
    Replace: com.trendyol:stove-$1$2
    

For Source Code (Kotlin/Java):

  1. Replace package imports:

    Find:    import com\.trendyol\.stove\.testing\.e2e\.(.*)
    Replace: import com.trendyol.stove.$1
    

  2. Replace fully qualified names:

    Find:    com\.trendyol\.stove\.testing\.e2e\.rdbms\.(postgres|mssql)
    Replace: com.trendyol.stove.$1
    

  3. Replace standalone.kafka:

    Find:    com\.trendyol\.stove\.testing\.e2e\.standalone\.kafka
    Replace: com.trendyol.stove.kafka
    

  4. Replace remaining testing.e2e references:

    Find:    com\.trendyol\.stove\.testing\.e2e\.([a-z.]+)
    Replace: com.trendyol.stove.$1
    

Step 4: Manual Verification

After automated replacement, verify:

  1. Build files compile - Run ./gradlew build or mvn compile
  2. Imports resolve - Check that all imports are valid
  3. Tests compile - Run ./gradlew compileTestKotlin or mvn test-compile
  4. Tests pass - Run your test suite

Example Migration

Before (0.19.0):

// build.gradle.kts
dependencies {
    testImplementation("com.trendyol:stove-testing-e2e:0.19.0")
    testImplementation("com.trendyol:stove-spring-testing-e2e:0.19.0")
    testImplementation("com.trendyol:stove-testing-e2e-kafka:0.19.0")
    testImplementation("com.trendyol:stove-testing-e2e-rdbms-postgres:0.19.0")
}

// Test code
import com.trendyol.stove.testing.e2e.system.TestSystem
import com.trendyol.stove.testing.e2e.kafka.kafka
import com.trendyol.stove.testing.e2e.rdbms.postgres.postgresql

After (0.20.0):

// build.gradle.kts
dependencies {
    testImplementation(platform("com.trendyol:stove-bom:0.20.0"))
    testImplementation("com.trendyol:stove")
    testImplementation("com.trendyol:stove-spring")
    testImplementation("com.trendyol:stove-kafka")
    testImplementation("com.trendyol:stove-postgres")
}

// Test code
import com.trendyol.stove.system.TestSystem
import com.trendyol.stove.kafka.kafka
import com.trendyol.stove.postgres.postgresql

Migration Checklist

  • [ ] Update all build.gradle.kts / build.gradle / pom.xml files
  • [ ] Replace all import statements in test source code
  • [ ] Replace all fully qualified package references
  • [ ] Update any documentation or scripts referencing old artifact names
  • [ ] Verify build compiles successfully
  • [ ] Run test suite to ensure everything works

Need Help?

If you encounter issues during migration: 1. Check the Migration Guide section below 2. Review the Getting Started guide for updated examples 3. Open an issue on GitHub


BaseApplicationContextInitializer Removed

BaseApplicationContextInitializer has been removed. Migrate to addTestDependencies:

Before (0.19.0):

class TestSystemInitializer : BaseApplicationContextInitializer({
    bean<TestSystemKafkaInterceptor<*, *>>()
    bean<MyService> { MyServiceImpl() }
})

// Usage
runApplication<MyApp>(args = params) {
    addInitializers(TestSystemInitializer())
}

After (0.20.0):

import com.trendyol.stove.addTestDependencies

runApplication<MyApp>(args = params) {
    addTestDependencies {
        bean<TestSystemKafkaInterceptor<*, *>>()
        bean<MyService> { MyServiceImpl() }
    }
}

This is simpler - no need to create a separate class.


HttpSystem: getResponse renamed to getResponseBodiless

The getResponse method in HttpSystem has been renamed to getResponseBodiless to better reflect its purpose - it returns a response without parsing the body.

Before:

http {
    getResponse("/api/endpoint") { response ->
        response.status shouldBe 200
    }
}

After:

http {
    getResponseBodiless("/api/endpoint") { response ->
        response.status shouldBe 200
    }
}


Notes

BridgeSystem API Enhancement

The BridgeSystem abstract class now has a new method getByType(type: KType) which is used by resolve() to support generic types. If you have a custom BridgeSystem implementation:

  • No action required if you only use simple types - the default implementation falls back to get(klass: KClass)
  • Override getByType(type: KType) if you want to support generic types like List<T>, Map<K,V>, etc.
// Example for custom bridge
override fun <D : Any> getByType(type: KType): D {
    // Use type.classifier for KClass
    // Use type.arguments for generic parameters
    return myDiFramework.resolve(type)
}

Dead Letter Topic Naming Convention (Spring Kafka)

Be aware that Spring Kafka changed the default DLT (Dead Letter Topic) naming convention between versions:

Spring Kafka Version DLT Suffix Example
2.x .DLT my-topic.DLT
3.x, 4.x -dlt my-topic-dlt

This is not a Stove change, but something to be aware of when writing Kafka tests across different Spring Kafka versions.

Optional: Disable Reporting

If you don't want the new reporting feature (not recommended), you can disable it:

TestSystem {
    reportingEnabled(false)
}

Dependency Updates

  • Spring Boot 4.x support (4.0.0+)
  • Spring Kafka 4.x support (4.0.0+)
  • Continued support for Spring Boot 2.7.x and 3.x
  • Continued support for Spring Kafka 2.9.x and 3.x

Full Changelog

See the GitHub Releases page for the complete list of commits and contributors.


Contributors

Thanks to all contributors who made this release possible!


Getting Started

dependencies {
    testImplementation("com.trendyol:stove:0.20.0")
    testImplementation("com.trendyol:stove-spring:0.20.0")
    // Add component-specific dependencies as needed
    testImplementation("com.trendyol:stove-spring-kafka:0.20.0")
}

For snapshot versions, add the snapshot repository:

repositories {
    maven("https://central.sonatype.com/repository/maven-snapshots")
}