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")
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:
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