Java/Junit中的异步单元测试——一个非常简单但不成功的示例

Java/Junit中的异步单元测试——一个非常简单但不成功的示例,java,unit-testing,asynchronous,junit,Java,Unit Testing,Asynchronous,Junit,在JavaScript中(例如使用Mocha),我可以编写异步单元测试,在最终回调中断言内容 这是通过将令牌函数(通常称为done)作为参数传递给测试方法来实现的 当调用函数时,测试框架理解测试已经完成 例如: it('succeeds or fails in the last callback', function(done) { var deferred = Q.defer(); setTimeout(function() { deferred.resolve(

在JavaScript中(例如使用Mocha),我可以编写异步单元测试,在最终回调中断言内容

这是通过将令牌函数(通常称为
done
)作为参数传递给测试方法来实现的

当调用函数时,测试框架理解测试已经完成

例如:

  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
      });

  });
  @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);
      });
  }
我看到Junit不适合这种情况。首先,测试方法无法处理参数,无论如何,如果我尝试,例如:

  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
      });

  });
  @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);
      });
  }
第二个
println
将不会执行,因为测试已经愉快地完成了很长一段时间

如果我作弊并放置
Thread.currentThread().sleep(500)在测试方法结束时,未来有机会完成,并执行断言+第二次打印输出

我有几个问题:

  • 最简单的Java设置是什么?在这个设置中,我可以在回调中验证断言,而不必阻止测试
  • 我必须完全放弃Junit吗
  • 是否有一个主流的测试框架(TestNG,也许?)允许以这种方式编写异步单元测试
顺便说一句,如果有人能给我推荐一种不用求助于
ScheduledFuture
的方法来编写这个Java示例测试,我也将不胜感激。
我尝试了一些关于
supplyAsync
的实验,但没有真正确定解决方案。

JS测试的异步性质是一个使测试复杂化的问题(我们的大脑以同步方式思考)。但是每个人(好吧)都对它保持和平,因为这是NodeJS的本质,不按照设计的方式使用这个工具可能是不明智的

Java没有这个“问题”(您必须显式地选择与NIO&Co合作以实现异步),这意味着您可以以正常(同步)方式编写测试。因此,您不应该在没有设计成这种方式的环境中应用JS模式,而应该以Java方式(或Rub方式、或.Net方式、或AlmostAnything方式)编写测试,而不必担心回调


但是,如果您碰巧测试了一个异步代码(例如,您使用JMS之类的MOM),那么您可能需要查看-special lib来测试这种情况。

当测试
CompletableFuture
时,您需要等待它的所有步骤完成


添加一个最终的
fut.join()

而不是
Thread.sleep
,在Java中进行异步测试是一个PITA。我通常将我的大多数层设计为同步测试,只有最顶层的应用层是异步的。我使用了一些同步。过去的测试机制(主要是wait/notify),但正如你所说的,它很糟糕。有一种说法可能重复,即由于语言的性质,被遗弃的JS程序员被迫使用回调。如果承诺(请原谅,CompletableFuture的承诺)来到Java,仅仅是因为它们是一个好主意)。如果主要的测试框架不注意,那就太糟糕了。无论如何,谢谢你的建议。但那个等待图书馆对我来说是个障碍。我说的对吗?等待-是的,它阻塞了。也许值得一看TestRules——它们可以改变JUnit处理测试结果的方式:这听起来非常有趣!让我们试试看。我把调用
fut.join
作为测试方法的最后一行,它就像一个符咒!我只能理解事情的一半,但无论如何我都很高兴。非常感谢。它与Thread.join一样工作。当前线程等待CompletableFuture结束它所包含的所有任务。然后返回最终结果
join
get
的不同之处在于
get
抛出选中的异常,而
join
不抛出选中的异常。