5

In JavaScript (for example using Mocha) I can write asynchronous unit tests which assert things inside the final callback.

This is accomplished by passing a token function (usually called done) as argument to the test method.

When the function is invoked, the testing framework understands that the test is completed.

For example:

  it('succeeds or fails in the last callback', function(done) {

      var deferred = Q.defer();
      setTimeout(function() { deferred.resolve('expected'); }, 500);

      deferred.promise.then(function(result) {
          assertEquals('expected', result);
          done();  // end of test
      });

  });

I see that Junit does not cater for such a scenario. First of all, test methods cannot handle an argument, and anyhow if I try for example:

  @Test
  public void testAssertionInsideContinuation() {

      Long now = System.currentTimeMillis();
      System.out.println("TimeMillis=" + now);

      CompletableFuture fut = new CompletableFuture();

      Executors.newScheduledThreadPool(1)
          .schedule(() -> { fut.complete("whatever"); }, 500, TimeUnit.MILLISECONDS);

      fut.thenRun(() -> {
          long then = System.currentTimeMillis();
          System.out.println("TimeMillis=" + then);
          assertTrue(then - now >= 500);
      });
  }

the second println will not be executed, because the test has happily completed long time before.

If I cheat and put a Thread.currentThread().sleep(500); at the end of the test method, then the future gets a chance to be completed, and the assertion + the second printout are executed.

I have a few questions:

  • what is the simplest Java setup in which I would be able to verify assertions inside callbacks/continuations/thenables (call them whatever you like) without having to block the test?
  • do I have to give up Junit altogether?
  • is there a mainstream testing framework (TestNG, maybe?) that allows writing async unit tests this way?

BTW, I'd also be grateful if anyone could suggest me a way to write this Java sample test without resorting to ScheduledFuture's. I have tried some experiment with supplyAsync without really nailing down a solution.

Marco Faustinelli
  • 3,734
  • 5
  • 30
  • 49
  • 1
    Doing async testing in Java is a PITA. I usually design most of my layers synchronous for testing, and only the topmost application layer would be async. I used some synchronization. mechanisms (mostly wait/notify) in the past for testing, but as you said it sucks. – Mister Smith Sep 20 '16 at 08:22
  • Possible duplicate of [How to use Junit to test asynchronous processes](https://stackoverflow.com/questions/631598/how-to-use-junit-to-test-asynchronous-processes) – tkruse Dec 06 '17 at 07:30

2 Answers2

3

When testing CompletableFuture you need to wait until all its steps are completed.

Instead of a Thread.sleep, add a final fut.join()

Federico Fissore
  • 712
  • 4
  • 18
  • This sounds darn interesting! Let's try. I put the invocation of `fut.join` as last line of the test method and it works like a charm! Can understand only half of what's going on, but I am happy anyhow. Thank you! – Marco Faustinelli Mar 23 '17 at 07:32
  • 1
    It works just as Thread.join. The current thread awaits for CompletableFuture to end all the tasks it's composed of. Then the final result is returned. `join` and `get` differ in that `get` throws checked exceptions, while `join` does not. – Federico Fissore Mar 23 '17 at 07:39
2

Async nature of JS tests is a problem that complicates testing a lot (our brain thinks in a synchronous way). But everyone (well, not everyone) makes peace with it because this is the nature of NodeJS and it's probably not wise to use the tool not in a way that it was designed.

Java doesn't have this "problem" (you'd have to explicitly choose to work with NIO & Co to go async) which means you can write tests in a normal (synchronous) way. So instead of applying JS patterns in the environment that's not designed to work this way you should write tests Java-way (or Rub-way, or .Net-way, or AlmostAnything-way) without worrying about callbacks.

But if you happen to test an async code (e.g. you use MOMs like JMS) then you may want to look at Awaitility - special lib to test such cases.

Stanislav Bashkyrtsev
  • 14,470
  • 7
  • 42
  • 45
  • 1
    There's that myth going around that derelict JS programmers are forced to use callbacks because of the nature of the language. If promises (pardon, CompletableFuture's) are coming to Java is just because they are a good idea). Too bad if the major testing frameworks do not take notice. – Marco Faustinelli Sep 20 '16 at 09:43
  • Anyway, thank you for the suggestion. But that Awaitility library looks like a blocking thing to me. Am I correct? – Marco Faustinelli Sep 20 '16 at 09:45
  • Awaitility - yes, it's blocking. Maybe it's worth looking at TestRules - they can alter the way JUnit treats test results: http://junit.org/junit4/javadoc/4.12/org/junit/rules/TestRule.html – Stanislav Bashkyrtsev Sep 20 '16 at 19:45