1

I am supposedly contributing to the integration tests of a library which relies on specs with the underlying implementation written in Scala. The testing is more bind towards the stream of events flowing with some timestamp attribute.

The following stub is the part of the actual implementation

private def eligibleForRecentPost(optionalPost: Option[SearchEntity]): Boolean = {
    optionalSearch.map(search => search.timestamp)
      .exists(searchTime => searchTime >= LocalDateTime.now()
        .minusDays(recencyDurationInDays).atZone(ZoneId.systemDefault).toInstant.toEpochMilli)
}

Now, the code that I would look for might be something like

// just a mock
when(LocalDateTime.now().minusDays(any)
    .atZone(ZoneId.systemDefault).toInstant.toEpochMilli)
    .thenReturn(1579625874972)

Note, I am aware that the search.timestamp in the test could be updated, but that would require updating the events after every recencyDurationInDays!!

But is there a better and reliable way to do this in specs2 and/or scala?

Edit: I must mention that I am not looking forward to changing the implementation such that the LocalDateTime is overridden/wrapped with another class.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • A shame that you can’t or won’t modify the implementation. For anyone who can, allow me to suggest that the answer is in this question: [Writing and testing convenience methods using Java 8 Date/Time classes](https://stackoverflow.com/questions/52956373/writing-and-testing-convenience-methods-using-java-8-date-time-classes). And/or [here](https://stackoverflow.com/questions/61470651/how-to-change-the-value-new-date-in-java). – Ole V.V. May 05 '20 at 13:11
  • @OleV.V. Indeed. I have gone through all such links on StackOverflow prior to posting the question and that was the reason for explicit edit as well. I would have loved to *ideally* modify the implementation but that's not always *practically* possible. :) – Naman May 05 '20 at 13:47

1 Answers1

6

There are tools like powermock which allows such things. But they are last resort solution, if you have to mock something in code that you have no control over.

Normally you would instead do something like:

trait Clock {

  def now(): LocalDateTime  
}

class DefaultClock extends Clock {

  def now(): LocalDateTime = LocalDateTime.now()
}

and then inject Clock instance into the code that uses it.

Instead of mocking static method you could just pass an instance that does whatever you wanted:

val simulatedNow = ... // calculate the right date for now
val clock = new Clock {
  def now() = simulatedNow
}
// inject clock
// run code and check assertion
Mateusz Kubuszok
  • 24,995
  • 4
  • 42
  • 64
  • Is that a suggestion to update the actual code as well? If so, my bad, I must have mentioned in the question, that's one direction I am not looking forward to. – Naman May 05 '20 at 12:19
  • 3
    Then your only hope is powermock. Normal mocking won't let you mock static methods. And even with powermock this is fragile as this literally requires JVM to modify bytecode of existing classes instead of creating proxies like normal mocks do. See this answer for more details https://stackoverflow.com/questions/10583202/powermockito-mock-single-static-method-and-return-object – Mateusz Kubuszok May 05 '20 at 12:27
  • Java has it's own clock implementation for this kind of problems and Cats too, it is the right way to code it, if you want to hack around then do whatever, but this is the right approach – ultrasecr.eth May 06 '20 at 11:28