Hello everyone, we currently have all of our tests...
# kotest
p
Hello everyone, we currently have all of our tests written with JUnit and kotest assertions. For our e2e tests we wanted to look into Kotest’s behaviour spec as it would improve readability by a big margin. We have the following scenario:
Copy code
class SomeE2ETest: E2ETestEnvironment { ... }
where E2ETestEnvironment is a class (annotated with
@SpringBootTest
) that holds common project config, e.g. a preconfigured http client to post something to the api. Converting this test simply to a Kotest test will not work as we can’t do the following:
Copy code
class SomeE2EKotest: E2ETestEnvironment(), BehaviorSpec({ ... })
because only one class may appear in a super type list. Long story short: I unfortunately couldn’t find any resource about how to do this with Kotest. Our main scenario is that we probably (currently) only translate the E2E Tests, so I’d like to find a way to access the
E2ETestEnvironment
from both JUnit tests and Kotest Tests. Thanks in advance!
s
You would probably need to make E2ETestEnvironment be an interface
👍 1
e
What I’ve done occasionally (if it’s not an option to make
E2ETestEnvironment
an interface) is to extend the spec.. In this case, something like:
Copy code
@SomeAnnotations
class EndToEndSpec(spec: BehaviorSpec() -> Unit): BehaviorSpec(
  {
    listeners(SomeListener)
    setup()
    spec()
    teardown()
  }
)
etc..
🤘 1
@sam do you know if there’s any downsides to this? In my case I extended FunSpec and it’s been working great. Since I’m using regular Spec methods for defining the tests, the IntelliJ plugin is happy as well.. Example:
s
I think that's a reasonable approach
👍 1
t
We created a custom annotation like
Copy code
@Transactional
@AutoConfigureMockMvc
@SpringBootTest(
  classes = [TestConfigs...],
  properties = ["spring.main.allow-bean-definition-overriding=true"],
)
annotation class ResourceIntegrationTest
various test configs defines beans to help with test setups and executions (though admittedly then our spring test config is different than production, but then those bean can be injected in your tests) as well as replacing some bean with stubs (for instance when we depend on external APIs)
🙌 2
p
Lots of great input here, thanks! To give a little bit more context, here is our env config:
Copy code
@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    properties = ["some properties"]
)
@ActiveProfiles("test")
@ContextConfiguration(initializers = [SomeApiInitializer::class])
@Import(AlwaysAuthorizedClientManager::class)
class E2ETestEnvironment(override val webClient: WebTestClient) {
    @Autowired
    lateinit var someApi: WireMockServer

    @Autowired
    lateinit var dealRepository: DealRepository

    @Autowired
    lateinit var objectMapper: ObjectMapper

    @Autowired
    lateinit var publisher: PubSubPublisherTemplate

    @Autowired
    lateinit var subscriberTemplate: PubSubSubscriberTemplate
    
    val ourOwnApi = OurOwnApi(webClient)
}

fun E2ETestEnvironment.resetState() {
    resetDb()
    resetPubSub()
    resetApis()
}

fun E2ETestEnvironment.save(deal: Deal): Deal = dealRepository.save(deal).block()!!

fun E2ETestEnvironment.pullPublishedPubSubMessages(
    topic: String,
    size: Int,
): List<PubsubMessage> = await.atMost(20, TimeUnit.SECONDS).until(...)
There’s quite some ugly code in there (look at all the lateinit vars. 🤮) that needs to be refactored anyway, but the main idea is to have the same configured spring context for the E2E Tests while also abstracting away clean up commands + querying data from external sources (db, pubsub). It would be really interesting how you guys are usually doing this - I see lots of room for improbement on my side. Anyway: Looking into the code I see that the E2E TestEnvironment is ofc only used by the E2E Tests (the ones that I want to move to Kotest), so still having interopability with JUnit is not a factor. @Emil Kantis Extending the Spec is for sure interesting, I’ll look into that! @thanksforallthefish also an interesting approach - our main problem here is that we also use properties from our Environment, so an annotation is not feasable.
t
for
resetState
you can look at kotest extensions, that way you can run it automatically before/after each test, spec (which in my head is the file) etc, as well as on demand (I guess, never really tried) for your autowired stuff and the client you can create a new test bean and inject those into your test (going composition over inheritance). this new bean can also have the methods
save
and
pullPublishedPubSubMessages
. eg:
Copy code
@Component
class TestBean(
autowired stuff here
) {
 val ourOwnApi = OurOwnApi(webClient)

 fun save(deal: Deal): Deal = dealRepository.save(deal).block()!!

 fun pullPublishedPubSubMessages(
    topic: String,
    size: Int,
): List<PubsubMessage> = await.atMost(20, TimeUnit.SECONDS).until(...)

  //could also have reset here, I just assumed you want to clean before/after tests, not in the middle, and for that I like extensions as it is transparent
}