Sam*_*Sam 187 java junit unit-testing asynchronous
如何测试使用Junit激发异步进程的方法?
我不知道如何让我的测试等待进程结束(它不是一个单元测试,它更像是一个集成测试,因为它涉及几个类而不仅仅是一个)
Mar*_*tin 183
另一种方法是使用CountDownLatch类.
public class DatabaseTest {
/**
* Data limit
*/
private static final int DATA_LIMIT = 5;
/**
* Countdown latch
*/
private CountDownLatch lock = new CountDownLatch(1);
/**
* Received data
*/
private List<Data> receiveddata;
@Test
public void testDataRetrieval() throws Exception {
Database db = new MockDatabaseImpl();
db.getData(DATA_LIMIT, new DataCallback() {
@Override
public void onSuccess(List<Data> data) {
receiveddata = data;
lock.countDown();
}
});
lock.await(2000, TimeUnit.MILLISECONDS);
assertNotNull(receiveddata);
assertEquals(DATA_LIMIT, receiveddata.size());
}
}
Run Code Online (Sandbox Code Playgroud)
注意你不能只使用与常规对象同步作为锁,因为快速回调可以在调用lock的wait方法之前释放锁.请参阅Joe Walnes撰写的这篇博客文章.
EDIT删除了CountDownLatch周围的同步块,感谢@jtahlborn和@Ring的评论
Joh*_*han 69
您可以尝试使用Awaitility库.它可以轻松测试您正在谈论的系统.
use*_*274 62
如果您使用CompletableFuture(在Java 8中引入)或SettableFuture(来自Google Guava),您可以在完成后立即完成测试,而不是等待预先设定的时间.您的测试看起来像这样:
CompletableFuture<String> future = new CompletableFuture<>();
executorService.submit(new Runnable() {
@Override
public void run() {
future.complete("Hello World!");
}
});
assertEquals("Hello World!", future.get());
Run Code Online (Sandbox Code Playgroud)
Cem*_*kas 45
恕我直言,让单元测试创建或等待线程等是不好的做法.你希望这些测试在几秒钟内运行.这就是为什么我想提出一个两步测试异步过程的方法.
Mat*_*att 17
我发现一种测试异步方法非常有用的方法是Executor在object-to-test的构造函数中注入一个实例.在生产中,执行程序实例配置为异步运行,而在测试中,它可以被模拟为同步运行.
所以假设我正在尝试测试异步方法Foo#doAsync(Callback c),
class Foo {
private final Executor executor;
public Foo(Executor executor) {
this.executor = executor;
}
public void doAsync(Callback c) {
executor.execute(new Runnable() {
@Override public void run() {
// Do stuff here
c.onComplete(data);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
在生产中,我将Foo使用Executors.newSingleThreadExecutor()Executor实例构建,而在测试中,我可能会使用执行以下操作的同步执行程序构造它 -
class SynchronousExecutor implements Executor {
@Override public void execute(Runnable r) {
r.run();
}
}
Run Code Online (Sandbox Code Playgroud)
现在我对异步方法的JUnit测试很干净 -
@Test public void testDoAsync() {
Executor executor = new SynchronousExecutor();
Foo objectToTest = new Foo(executor);
Callback callback = mock(Callback.class);
objectToTest.doAsync(callback);
// Verify that Callback#onComplete was called using Mockito.
verify(callback).onComplete(any(Data.class));
// Assert that we got back the data that we expected.
assertEquals(expectedData, callback.getData());
}
Run Code Online (Sandbox Code Playgroud)
JUnit 5 有Assertions.assertTimeout(Duration, Executable)/ assertTimeoutPreemptively()(请阅读每个文件的 Javadoc 以了解差异),Mockito 有verify(mock, timeout(millisecs).times(x)).
Assertions.assertTimeout(Duration.ofMillis(1000), () ->
myReactiveService.doSth().subscribe()
);
Run Code Online (Sandbox Code Playgroud)
和:
Mockito.verify(myReactiveService,
timeout(1000).times(0)).doSth(); // cannot use never() here
Run Code Online (Sandbox Code Playgroud)
管道中的超时可能是不确定的/脆弱的。所以要小心。
测试线程/异步代码没有任何内在错误,特别是如果线程是您正在测试的代码的重点.测试这些东西的一般方法是:
但这是一次测试的很多样板.更好/更简单的方法是使用ConcurrentUnit:
final Waiter waiter = new Waiter();
new Thread(() -> {
doSomeWork();
waiter.assertTrue(true);
waiter.resume();
}).start();
// Wait for resume() to be called
waiter.await(1000);
Run Code Online (Sandbox Code Playgroud)
这种CountdownLatch方法的好处在于,它更简洁,因为任何线程中发生的断言失败都被正确地报告给主线程,这意味着测试失败了.这里比较了CountdownLatchConcurrentUnit 的方法.
我还为那些想要学习更多细节的人写了一篇关于这个主题的博客文章.
| 归档时间: |
|
| 查看次数: |
135267 次 |
| 最近记录: |