android - 如何测试从其他类方法内部获取值的方法
问题描述
每次我遇到 nullPointerException 或其他 Mockito 错误时,我都尝试了几种方法来模拟和监视这些值并调用下面的方法。由于我是 Mockito 和 android 本身的新手,请告诉我如何为下面的方法编写 AndroidTest 用例,该方法也使用 Retrofit API 调用。我正在尝试对整个方法进行编码。提前致谢
public LiveData<BaseModel<Onboarding>> updateUser(Result item) {
final MutableLiveData<BaseModel<Onboarding>> data = new MutableLiveData<>();
final BaseModel<Onboarding> dataWrapper = new BaseModel<>();
Map<String, String> headers = WaApplication.getContext().getRequestHeaders();
String tenantId = WaApplication.getContext().getTenantId();
service.updateUser(tenantId,headers, item).enqueue(new Callback<UserOnboarding>() {
@Override
public void onResponse(Call<Onboarding> call, Response<Onboarding> response) {
dataWrapper.setData(response.body());
dataWrapper.setResponseCode(response.code());
data.setValue(dataWrapper);
LogUtils.d(TAG, "updateUser:HTTPS_ONBOARDING_API_ENDPOINT: "+ BuildConfig.HTTPS_ONBOARDING_API_ENDPOINT);
LogUtils.d("OnBoardingService onRes", new Gson().toJson(response.body()));
}
@Override
public void onFailure(Call<Onboarding> call, Throwable t) {
LogUtils.e(TAG, "OnBoardingService API Failed. Error - " + t.getMessage());
data.setValue(null);
}
});
return data;
}
解决方案
TL;博士:
@Rule
public InstantTaskExecutorRule InstantTaskExecutorRule = new InstantTaskExecutorRule();
我假设您要编写单元测试(在 JVM 上运行)而不是仪器测试(在 Android 设备上运行)。
在单元测试中,您对data.setValue()的调用将导致:
java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.os.Looper.getMainLooper(Looper.java)
为了能够在 JVM 上的单元测试中使用 Android 类,将其android.jar
添加到类路径中。这个 jar 包含所有 Android 类,但没有它们的实现。因此,如果您执行它的任何代码,它总是会抛出上述异常。Android 方法调用的真正实现仅在 Android 设备上可用。
因此,通常对于单元测试,您必须使用例如 Mockito 来模拟方法调用。
或者,您可以将测试作为 Instrumentation 测试执行(将其放在androidTest
源集中)。然后它们在 Android 设备上执行。
在这种情况下LiveData
,您可以通过更改LiveData
发送方式来规避这种情况。谷歌为这种情况提供了一个 JUnit TestRule,你必须向你的build.gradle
和TestRule
你的测试类添加一个工件
构建.gradle:
testImplementation 'androidx.arch.core:core-testing:2.1.0'
测试类:
@Rule
public InstantTaskExecutorRule InstantTaskExecutorRule = new InstantTaskExecutorRule();
在旁注中:
- 不要
Retrofit
在单元测试中进行网络或对真实 URL 的调用。最好将其指向baseUrl
模拟服务器并使用OkHttp MockWebServer伪造您的网络响应 - 如果你没有你想把它放在那里的
updateUser
方法。ViewModel
因为这正是 a 的用例ViewModel
。 - 如果你
updateUser
在 a 中有方法ViewModel
,那么你不需要返回LiveData
.data
LiveData
应该是你的一个实例变量,然后ViewModel
你可以在你的Activity
or中观察它Fragment
。 - 您可能希望使用 android de-facto log 标准Timber而不是自定义的
LogUtils
.
推荐阅读
- android - data:image/png;base64 转换,但返回 null (运行代码如下)
- docker - 使用 docker run 无法从其他容器访问容器
- apache-spark - 防止 Spark Shell 中结构化流的进度输出
- mysql - 为什么这个 MYSQL 查询中的子查询比这个慢很多?
- apache-flink - KeyedStream中的max和maxBy有什么区别
- php - 有没有办法通过任何编码语言发送机密邮件?
- laravel - Laravel:所有表单都抛出 PostTooLargeException
- html - 文本在多边形函数内被剪切并且不接受任何样式命令
- docker - 我可以在 docker 容器中构建一个可以从外部 docker 链接到的动态库吗?
- c++ - 如何将 Class 对象传递给 CUDA 内核