gpt4 book ai didi

java - 单元测试异步操作

转载 作者:行者123 更新时间:2023-11-30 00:17:17 25 4
gpt4 key购买 nike

我正在尝试对将工作委托(delegate)给后台执行程序的代码进行单元测试。在重构删除方法以返回 ID 列表后,我在单元测试中遇到了问题。当方法抛出 sqlexception 失败时应验证行为的测试。

我以前没有在我的代码中使用过 Futures,所以如果这个设计有缺陷,请原谅我。

以下是我的代码。

任务交互器.java:

public class TaskInteractor extends AbstractInteractor
implements TaskContract.Interactor {

private final TaskRepository mRepository;

@Inject
public TaskInteractor(WorkerThread workerThread, MainThread mainThread, TaskRepository repository) {
super(workerThread, mainThread);
this.mRepository = repository;
}
...
@Override
@android.support.annotation.MainThread
public void deleteTasks(@NonNull final List<Task> tasks, @NonNull final DeleteTaskCallback callback) {
try {
final Future<List<String>> future = mWorkerThread.execute(() ->
mRepository.softDeleteAllInTransaction(tasks));
mMainThread.post(() -> {
try {
callback.onDeleteSuccess(future.get());
} catch (InterruptedException | ExecutionException e) {
Timber.e(e);
throw new RuntimeException(e.getCause());
}
});
} catch (final SQLiteAbortException e) {
mMainThread.post(() -> { callback.onAbortException(e); });
throw e;
} catch (final SQLiteConstraintException e) {
mMainThread.post(() -> { callback.onConstraintException(e); });
throw e;
} catch (final Exception e) {
mMainThread.post(() -> { callback.onFailure(e); });
throw new RuntimeException(e);
}
}
}

我收到以下消息:

Wanted but not invoked:
mDeleteTaskCallbackMock.onAbortException(
<any android.database.sqlite.SQLiteAbortException>
);
-> at com.example.ui.task.ExaminationInteractorTest.whenDeleteFailWithSQLiteAbortException_shouldCallOnAbortFailureCallback(ExaminationInteractorTest.java:173)

However, there was exactly 1 interaction with this mock:
mDeleteTaskCallbackMock.onFailure(
java.lang.RuntimeException:
android.database.sqlite.SQLiteAbortException
);
-> at com.example.ui.examination.ExaminationInteractor.lambda$deleteExaminations$8(ExaminationInteractor.java:79)

这是我的一个测试。

@Test
public void whenDeleteFailWithSQLiteConstraintException_shouldCallOnConstraintFailureCallback() throws Exception {
doThrow(new SQLiteConstraintException()).when(mRepositoryMock).softDeleteAllInTransaction(ArgumentMatchers.<Task>anyList());

List<Task> tasks = Arrays.asList(TEST_TASKS);

mInteractor.deleteTasks(tasks, mDeleteTaskCallback);
verify(mDeleteTaskCallback).onConstraintException(
any(SQLiteConstraintException.class));
}

执行由具有以下实现的测试替身完成。

FakeWorkerThread.java:

/**
* Just runs the commands without invoking other threads
*/
public class FakeWorkerThread implements WorkerThread {

@Override
public void execute(Runnable interactor) {
interactor.run();
}

@Override
public <T> Future<T> execute(Callable<T> callable) throws Exception {
RunnableFuture<T> ftask = new FutureTask<T>(callable);
execute(ftask);
return ftask;
}
}

最佳答案

在此 fragment 中:

try {
final Future<List<String>> future = mWorkerThread.execute(() ->
mRepository.softDeleteAllInTransaction(tasks));
mMainThread.post(() -> {
try {
callback.onDeleteSuccess(future.get()); // LINE 1
} catch (InterruptedException | ExecutionException e) {
Timber.e(e);
throw new RuntimeException(e.getCause()); // LINE 2
}
});

您正在将 Callable 传递给工作人员。它的 call() 抛出的任何异常都将包装在 ExecutionException 中并存储在 Future 中。然后,当您调用 future.get() 并且任务完成时,它会抛出 ExecutionException

所以发生的事情如下:

  1. future.get() 抛出一个 ExecutionException 包装一个 SQLiteConstraintException (LINE 1);
  2. SQLiteConstraintException 被包裹在 RuntimeException 中(第 2 行);
  3. 由于 RuntimeException 与前 2 个 catch 子句不匹配,它最终在最后一个 catch 中被处理:catch (final Exception e) {};
  4. 如测试输出所示,mDeleteTaskCallbackMock(RuntimeException) 被调用;

我建议您更改为以下内容:

try {
callback.onDeleteSuccess(future.get());
} catch (InterruptedException | ExecutionException e) {
Timber.e(e);
throw e.getCause(); //<--- throw the unwrapped exception
}

关于java - 单元测试异步操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47014667/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com