Search code examples
mongodbspring-boottestingkotlinjunit4

Kotlin shared companion object across junit tests


I have an integration test where I start an embedded MongoDB as a companion object. I want to reuse this piece of code and I am not sure if inheritance is the way to go (if possible).

It is a Spring Boot application:

This is my test:

@RunWith(SpringRunner::class)
@SpringBootTest
class RequestRepositoryTest {

@Autowired lateinit var requestRepository: RequestRepository

  companion object {
    private val starter = MongodStarter.getDefaultInstance()
    private var _mongod: MongodProcess? = null
    private var _mongo: MongoClient? = null

    @BeforeClass
    @JvmStatic fun beforeTest(){
        val port = 27017
        val _mongodExe = starter.prepare(MongodConfigBuilder()
                .version(Version.Main.DEVELOPMENT)
                .net(Net("localhost", port, Network.localhostIsIPv6()))
                .build())
        _mongod = _mongodExe.start()
        _mongo = MongoClient("localhost", port)
    }

    @AfterClass
    @JvmStatic fun afterTest(){
        _mongod?.stop()
    }
  }

  @Test
  fun store() {
    val id = requestRepository.store(Request(requestId = "123"))
    assertNotNull(id)
  }

}

My repository class:

@Repository
class RequestRepository @Autowired constructor(val datastore: Datastore) 
{
  fun store(request : Request) : String = 
  datastore.save(request).id.toString()
}

So my question is which is the 'correct' way to go about this in Kotlin.

Update edit: As an external object the test now looks a lot cleaner and the JUnit external resource is completely reusable across test classes:

Thanks @Lovis

@RunWith(SpringRunner::class)
@SpringBootTest
class RequestRepositoryTest {

  companion object {
      @ClassRule
      @JvmField
      val mongoServer = MongoServer
  }

  @Autowired lateinit var requestRepository: RequestRepository

  @Test
  fun store() {
    val id = requestRepository.store(Request(requestId = "123"))
    assertNotNull( id )
    assertTrue { ObjectId.isValid(id) }
  }

}


Solution

  • You should be able to achieve what you want using jUnit's @ClassRule and ExternalResource. No Kotlin magic needed :-)

    Define an object in a separate File:

    object MongoServer  : ExternalResource() {
        @Throws(Throwable::class)
        override fun before() {
            // before class
        }
    
        override fun after() {
           // after class
        }
    }
    

    Then use it within each test:

    companion object {
        @ClassRule
        @JvmField
        val mongoServer = MongoServer
    }
    

    The ClassRule annotation does the trick here, the companion object is necessary to make it static and the @JvmField annotation is necessary to make the field public. Those are restrictions by jUnit's rule system.