Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ScalaTest: treat Instants as equal when in the same millisecond

I have a case class that I serialize to JSON, and a test case that checks that round-tripping works.

Buried deep inside the case class are java.time.Instants, which I put into JSON as their epoch milliseconds.

Turns out that an Instant actually has nanosecond precision and that gets lost in translation, making the test fail because the timestamps are slightly off now.

Is there an easy way to make Scalatest ignore the difference? I just want to fix the test, the loss in precision is totally acceptable for the application.

like image 819
Thilo Avatar asked Jan 28 '23 00:01

Thilo


2 Answers

We use Clock.instant to know the current time instead of Instant.now to avoid this problem. So the code for the class should be this way

class MyClass(clock: Clock) {
  def getResult(): Result = {
    Result(clock.instant)
  }
}

and at the test, we mock clock.instant to guarantee we check the exact same time.

class MyClassTest {
  val customTime = Instant.now
  val clock = mock[Clock]
  clock.instant() returns customTime

  // test
  val myClass = new MyClass(clock)
  val expectedResult = Result(customTime)
  myClass.getResult ==== expectedResult
}
like image 114
Mahmoud Hanafy Avatar answered Feb 16 '23 03:02

Mahmoud Hanafy


Truncate to millisecond before comparing

I am not sure it will help in your situation, but in case and for anyone else reading along: the correct way of comparing two Instant objects considering only millisecondes and coarser is to truncate each to millisecond precision (here in Java):

    Instant instant1 = Instant.parse("2018-12-14T08:25:54.232235133Z");
    Instant instant2 = Instant.parse("2018-12-14T08:25:54.232975217Z");
    if (instant1.truncatedTo(ChronoUnit.MILLIS).equals(instant2.truncatedTo(ChronoUnit.MILLIS))) {
        System.out.println("Equal to the millisecond");
    } else {
        System.out.println("Not equal to the millisecond");
    }

Output:

Equal to the millisecond

If you know for a fact that one of them has already been truncated in its roundtrip through JSON (and you consider it a requirement that it must be), you don’t need to truncate that one again, of course.

Use a Clock that has only millisecond presicion

Using a Clock for testing is often a good idea. It can help you write reproducible tests. You can easily have a clock that only counts milliseconds:

    Clock c = Clock.tickMillis(ZoneOffset.UTC);
    System.out.println(c.instant());
    System.out.println(Instant.now(c));

Output when running just now:

2018-12-14T10:48:47.929Z
2018-12-14T10:48:47.945Z

As you see, the generated Instant objects have only three decimals on the seconds, that is, millisecond precision and nothing finer. When you only use the Clock for drawing Instants, it doesn’t matter which time zone you pass to tickMillis.

like image 28
Ole V.V. Avatar answered Feb 16 '23 03:02

Ole V.V.