Home > OS >  assign "val" for unit test in kotlin
assign "val" for unit test in kotlin

Time:02-02

I am writing unit tests in kotlin, for this purpose I need to assign value on a "val", here is the simplified version of the code:

@Entity
@Table(name = "Request")
data class Request(

    @Column(name = "Name")
    val name: String,
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    var id: Long? = null

    @CreationTimestamp
    @Column(name = "Created")
    val created: LocalDateTime = LocalDateTime.now()
}

@Test
fun `test one`() {
    val name = RandomStringUtils.randomNumeric(10)
    val id = Random.nextLong(100)
    val created = LocalDateTime.now().minusHours(48)
    
    val request = Request(playerUid = playerUid).apply {
        id = id
        created = created
    }
}

it has an compile error when assigning "created" in the test. How should I manage this unit test since I need to set my desire "created" value? (I can not touch any part of the "Request class")

CodePudding user response:

If you cannot change any part of the Request class then you will not be able to change created.

You will either need to test created by using an approximate test range (created needs to be 0<now<2s sort of thing)

It is a design flaw to encode a static accessor to functions like LocalDateTime.now() - this should be set externally in a service class. If you really cannot do that, then here is another hacky approach:

  1. add a CLOCK object somewhere (does not need to be in a companion object) but ultimately you have to change the created assignment:
@Entity
@Table(name = "Request")
data class Request(
    @Column(name = "Name")
    val name: String,
) {
    companion object {
        /** used for carrying a Clock only in the case of tests **/
        val CLOCK = ThreadLocal<Clock>()
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    var id: Long? = null

    @CreationTimestamp
    @Column(name = "Created")
    val created: LocalDateTime = LocalDateTime.now(CLOCK.get() ?: Clock.systemUTC())
}

Normally you don't touch that CLOCK but in the Unit Tests you define a

private val fixedClock = Clock.fixed(Instant.parse("2022-08-29T08:20:50Z"), ZoneOffset.UTC)

then you need

@BeforeEach
fun beforeEach() {
    // this is necessary because the serialization of MemberMentorCommon.weeksOnPlan uses a clock
    CLOCK.getOrSet { fixedClock }
}

@AfterEach
fun afterEach() {
    CLOCK.remove()
}

Yes, ThreadLocals are nasty, but this allows you to change the behaviour of the Request class to override the now() function

  • Related