使用 Jmockit 验证 FutureCallback 效果的最佳方法
Best approach for verifying a FutureCallback effect with Jmockit
在 Jmockit 中验证回调效果的首选机制是什么?
例如,假设我有这个 class。
class ResultGenerator {
AsyncLauncher asyncLauncher = new AsyncLauncher();
public void getResultAsync(final ResultSignal resultSignal) {
asyncLauncher.getResult(new FutureCallback<Result>() {
@Override
public void onSuccess(@Nullable Result result) {
resultSignal.success(result);
}
@Override
public void onFailure(Throwable t) {
resultSignal.failure();
}
});
}
}
在为 ResultGenerator#getResultAsync
编写测试时如何验证 resultSignal.success(result)
?
例如
@RunWith(JMockit.class)
public class ResultGeneratorTest {
// Synchronous invocation, mocked AsyncLauncher
@Test
public void testGetResultAsync(@Mocked final ResultSignal resultSignal, @Mocked final Result result) throws Exception {
new MockUp<AsyncLauncher>() {
@Mock
void getResult(FutureCallback<Result> futureCallback) {
futureCallback.onSuccess(result);
}
};
ResultGenerator resultGenerator = new ResultGenerator();
resultGenerator.getResultAsync(resultSignal);
new Verifications() {{
resultSignal.success((Result) any); times = 1;
resultSignal.failure(); times = 0;
}};
}
// Asynchronous invocation, real AsyncLauncher in use
@Test
public void testGetResultAsyncDelayed(@Mocked final Result result) throws Exception {
final AtomicBoolean latch = new AtomicBoolean(false);
MockUp<ResultSignal> resultSignalMockUp = new MockUp<ResultSignal>() {
@Mock(invocations = 1)
public void success(Result result) {
latch.set(true);
}
@Mock(invocations = 0)
public void failure() {
latch.set(true);
}
};
ResultGenerator resultGenerator = new ResultGenerator();
final ResultSignal resultSignal = resultSignalMockUp.getMockInstance();
resultGenerator.getResultAsync(resultSignal);
Awaitility.await().untilTrue(latch);
}
}
一些注意事项:
ResultGenerator
是您的 SUT(被测系统),您不应该模拟内部结构
ResultSignal
是测试合作者,自然要mock出来
- 因为您可以验证功能本身,所以单元测试理论中唯一的 "correct" 解决方案是模拟协作者
- 您必须确保正确处理超时,否则测试可能永远不会结束
所以一种可能的解决方案是:
@Test
public void getResultAsync_ShouldNotifyResultSignal() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
ResultGenerator generator = new ResultGenerator();
generator.getResultAsync(new MyResultSignal(latch));
assertTrue(latch.await(1, SECONDS));
}
private static final class MyResultSignal implements ResultSignal {
private final CountDownLatch latch;
private MyResultSignal(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void success(Result result) {
latch.countDown();
}
@Override
public void failure() {}
}
在 Jmockit 中验证回调效果的首选机制是什么?
例如,假设我有这个 class。
class ResultGenerator {
AsyncLauncher asyncLauncher = new AsyncLauncher();
public void getResultAsync(final ResultSignal resultSignal) {
asyncLauncher.getResult(new FutureCallback<Result>() {
@Override
public void onSuccess(@Nullable Result result) {
resultSignal.success(result);
}
@Override
public void onFailure(Throwable t) {
resultSignal.failure();
}
});
}
}
在为 ResultGenerator#getResultAsync
编写测试时如何验证 resultSignal.success(result)
?
例如
@RunWith(JMockit.class)
public class ResultGeneratorTest {
// Synchronous invocation, mocked AsyncLauncher
@Test
public void testGetResultAsync(@Mocked final ResultSignal resultSignal, @Mocked final Result result) throws Exception {
new MockUp<AsyncLauncher>() {
@Mock
void getResult(FutureCallback<Result> futureCallback) {
futureCallback.onSuccess(result);
}
};
ResultGenerator resultGenerator = new ResultGenerator();
resultGenerator.getResultAsync(resultSignal);
new Verifications() {{
resultSignal.success((Result) any); times = 1;
resultSignal.failure(); times = 0;
}};
}
// Asynchronous invocation, real AsyncLauncher in use
@Test
public void testGetResultAsyncDelayed(@Mocked final Result result) throws Exception {
final AtomicBoolean latch = new AtomicBoolean(false);
MockUp<ResultSignal> resultSignalMockUp = new MockUp<ResultSignal>() {
@Mock(invocations = 1)
public void success(Result result) {
latch.set(true);
}
@Mock(invocations = 0)
public void failure() {
latch.set(true);
}
};
ResultGenerator resultGenerator = new ResultGenerator();
final ResultSignal resultSignal = resultSignalMockUp.getMockInstance();
resultGenerator.getResultAsync(resultSignal);
Awaitility.await().untilTrue(latch);
}
}
一些注意事项:
ResultGenerator
是您的 SUT(被测系统),您不应该模拟内部结构ResultSignal
是测试合作者,自然要mock出来- 因为您可以验证功能本身,所以单元测试理论中唯一的 "correct" 解决方案是模拟协作者
- 您必须确保正确处理超时,否则测试可能永远不会结束
所以一种可能的解决方案是:
@Test
public void getResultAsync_ShouldNotifyResultSignal() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
ResultGenerator generator = new ResultGenerator();
generator.getResultAsync(new MyResultSignal(latch));
assertTrue(latch.await(1, SECONDS));
}
private static final class MyResultSignal implements ResultSignal {
private final CountDownLatch latch;
private MyResultSignal(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void success(Result result) {
latch.countDown();
}
@Override
public void failure() {}
}