Home > Software design >  how to test equality of data class with OffsetDateTime attribute?
how to test equality of data class with OffsetDateTime attribute?

Time:11-21

I have an attribute in a DTO and Entity defined like this:

val startDate: OffsetDateTime,

The dto has a toEntity method:

data class SomeDTO(
  val id: Long? = null,
  val startDate: OffsetDateTime,
  ) {

    fun toEntity(): SomeEntity {
      return SomeEntity(
        id = id,
        startDate = startDate,
      )
    }
}

And a controller

@RestController
@RequestMapping("/some/api")
class SomeController(
  private val someService: SomeService,
) {
  @PostMapping("/new")
  @ResponseStatus(HttpStatus.CREATED)
  suspend fun create(@RequestBody dto: SomeDTO): SomeEntity {
    return someService.save(dto.toEntity())
  }
}

And I have a failing test:

  @Test
  fun `create Ok`() {
    val expectedId = 123L

    val zoneId = ZoneId.of("Europe/Berlin")
    val dto = SomeDTO(
      id = null,
      startDate = LocalDate.of(2021, 4, 23)
        .atStartOfDay(zoneId).toOffsetDateTime(),
    )
    val expectedToStore = dto.toEntity()
    val stored = expectedToStore.copy(id = expectedId)

    coEvery { someService.save(any()) } returns stored

    client
      .post()
      .uri("/some/api/new")
      .contentType(MediaType.APPLICATION_JSON)
      .bodyValue(dto)
      .exchange()
      .expectStatus().isCreated
      .expectBody()
      .jsonPath("$.id").isEqualTo(expectedId)

    coVerify {
      someService.save(expectedToStore)
    }
  }

The test fails for the coVerify because the startDate does not match:

Verification failed: ...
... arguments are not matching:
[0]: argument: SomeEntity(id=null, startDate=2021-04-22T22:00Z),
matcher: eq(SomeEntity(id=null, startDate=2021-04-23T00:00 02:00)),
result: -

Semantically, the startDates match, but the timezone is different. I wonder how I can enforce coVerify to either use a proper semantic comparison for type OffsetDateTime or how I can enforce the internal format of OffsetDateTime=? Or what other approach should we use to verify the expectedToStore value is passed to someService.save(...) ?

I could use withArgs but it is cumbersome:

coVerify {
  someService.save(withArg {   
    assertThat(it.startDate).isEqualTo(expectedToStore.startDate)
    // other manual asserts
 })
}

CodePudding user response:

tl;dr

Add this to your application.properties:

spring.jackson.deserialization.adjust-dates-to-context-time-zone=false

This way, the offset will be deserialized as retrieved and not altered.


I created a (slightly modified) reproduction repository on GitHub. Inside the Controller the value of dto.startDate is already 2021-04-22T22:00Z, thus at UTC.

By default, the serialization library used "Jackson" aligns all offsets during deserialization to the same configured offset. The default offset used is 00:00 or Z, which resembles UTC.

You can enable / disable this behaviour over the property spring.jackson.deserialization.adjust-dates-to-context-time-zone={true false} and set the timezone with spring.jackson.time-zone=<timezone>

Alternatively, you can force to align the offset with an other timezone during deserialization:

spring.jackson.time-zone=Europe/Berlin

This way, the offset will be the aligned with the timezone Europe/Berlin.

  • Related