0

I have a problem getting Spring Boot 2.0.5 to work nicely with Kotlintest 3.1.10.

I made a test project illustrating the problem I have. The project is a Spring Boot 2 application with two entities, ShoppingOrder and OrderLine (to be totally unimaginative).

There is also a test case ShoppingOrderSpec which just tests the mapping by storing and retrieving the Order.

The testcase is configured like this:

@ExtendWith(SpringExtension::class)
@Transactional
@SpringBootTest
class ShoppingOrderSpec : WordSpec() {
    override fun listeners() = listOf(SpringListener)

The test case is using the SpringExtension by Spring to hook into the JUnit 5 engine. It also uses the SpringListener and Wordspec from Kotlintest to structure the tests and do the assertions.

The SpringListener correctly autowires the dependencies, but somehow the transaction is not being created.

Running the testcase gives the following stack-trace:

2018-10-12 10:54:14.329 INFO 59374 --- [intest-engine-0] com.example.demo.ShoppingOrderSpec : Started ShoppingOrderSpec in 4.478 seconds (JVM running for 7.421) org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.demo.ShoppingOrder.lines, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582) ... at com.example.demo.ShoppingOrderSpec$1$1.invoke(ShoppingOrderSpec.kt:35) at com.example.demo.ShoppingOrderSpec$1$1.invoke(ShoppingOrderSpec.kt:19)

So, somehow the org.springframework.transaction.annotation.Transactional annotation does not seem to work, as removing the annotation, just gives the same response.

Anyone any ideas how to get the @Transactional being applied and respected?

1 Answers1

0

You can't use JUnit Jupiter extensions with KotlinTest as they are different engines. Junit Jupiter is an implementation on top of Junit Platform, like KotlinTest is, but anything written specifically for Jupiter won't work with KotlinTest. Anything written for Junit Platform should work however.

Unfortunately, the naming choices by the JUnit team are poor imo, and so people think JUnit Jupiter is the same thing as JUnit Platform.

Anyway, those

@ExtendWith(SpringExtension::class)
@Transactional
@SpringBootTest

extensions are not going to mean anything to KotlinTest, anymore than they would for Spek or whatever. ExtendWith is a Jupiter specific annotation that tells it to use the SpringExtension class. The KotlinTest equivilent is SpringListener which you've already wired in.

I'm not sure if @SpringBootTest will be picked up or not by Spring. Support may need to be added for that depending on what it does.

Finally @Transactional works by creating proxies on the methods, but since in more advanced testing frameworks like KotlinTest, the test containers are not methods, but just arbitrary functions, it won't be able to intercept.

I think in this case, you might need to create a proper method and annotate that, or try using the AnnotationSpec rather than StringSpec or whatever other spec base class you are using, which uses actual methods that you could annotate.

sksamuel
  • 16,154
  • 8
  • 60
  • 108
  • Thanks for the clear answer! Has anyone already tried getting the @Transactional annotation working with KotlinTest? Looking at the code it should be possible to already start a transaction through the Springframework infrastructure code in beforeTest? And rollback/commit in the afterTest? Or am I missing something here and has this been tried before? If you have any pointers for this, that would be appreciated. – mblankestijn Oct 15 '18 at 06:07
  • Yes it would be possible, but not via @Transaction annotation. An extension could be created which invokes the transaction before the test and then commits/rolls back after the test. – sksamuel Oct 17 '18 at 22:52