【发布时间】:2019-11-19 13:41:03
【问题描述】:
我需要对内部使用 RxJava Singles 的 Singleton 类的方法进行单元测试,并使用 PowerMock 测试框架来模拟静态类和方法。我尝试了各种方法来模拟 Schedulers.io() 和 AndroidSchedulers.mainThread() 方法,但它不起作用。我在 UserApi.verifyUserData() 方法中的 .subscribeOn(Schedulers.io()) 行收到 java.lang.NullPointerException 错误。
单例类 UserApi(被测类)
final public class UserApi {
private CompositeDisposable compositeDisposable;
private String userID;
//private final SchedulerProvider schedulerProvider;
private UserApi(String userId) {
super();
this.userID = userId;
//this.schedulerProvider = schedulerProvider;
}
public static UserApi getInstance() {
return SingletonHolder.sINSTANCE;
}
private static final class SingletonHolder {
private static final UserApi sINSTANCE;
static {
String uuid = UUID.randomUUID().toString();
sINSTANCE = new UserApi(uuid);
}
}
// Rest Api call
public void verifyUserData(byte[] doc, byte[] img) {
this.compositeDisposable = new CompositeDisposable();
String docStr = Base64.encodeToString(doc, Base64.NO_WRAP);
String imgStr = Base64.encodeToString(img, Base64.NO_WRAP);
final Disposable apiDisposable = IdvManager.getInstance().getUserManager().verifyUserData(docStr, imgStr)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<JsonObject>() {
@Override
public void accept(JsonObject verifyResponse) throws Exception {
pollResult();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable error) throws Exception {
// handle error code...
}
});
this.compositeDisposable.add(apiDisposable);
}
private void pollResult() {
// code here...
}
}
UserManager 类和接口
public interface UserManager {
Single<JsonObject> verifyUserData(String docStr, String imgStr);
}
final class UserManagerImpl implements UserManager {
private final UserService userService;
UserManagerImpl(final Retrofit retrofit) {
super();
this.userService = retrofit.create(UserService.class);
}
@Override
public Single<JsonObject> verifyUserData(String docStr, String imgStr) {
// Code here...
}
}
单元测试
@RunWith(PowerMockRunner.class)
@PrepareForTest({IdvManager.class, Base64.class, Schedulers.class, AndroidSchedulers.class, UserApi.class})
public class UserApiTest {
@Mock
public UserManager userManager;
@Mock
private Handler handler;
private IdvManager idvManager;
private Schedulers schedulers;
private UserApi spyUserApi;
private TestScheduler testScheduler;
private String userID;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
handler = new Handler();
PowerMockito.suppress(constructor(IdvManager.class));
// mock static
PowerMockito.mockStatic(IdvManager.class);
PowerMockito.mockStatic(Schedulers.class);
PowerMockito.mockStatic(AndroidSchedulers.class);
PowerMockito.mockStatic(Base64.class);
// Create mock for class
idvManager = PowerMockito.mock(IdvManager.class);
schedulers = PowerMockito.mock(Schedulers.class);
PowerMockito.when(IdvManager.getInstance()).thenReturn(IdvManager);
when(idvManager.getUserManager()).thenReturn(userManager);
spyUserApi = PowerMockito.spy(UserApi.getInstance());
// TestSchedulerProvider testSchedulerProvider = new TestSchedulerProvider(testScheduler);
when(Base64.encodeToString((byte[]) any(), anyInt())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return java.util.Base64.getEncoder().encodeToString((byte[]) invocation.getArguments()[0]);
}
});
when(schedulers.io()).thenReturn(testScheduler);
when(AndroidSchedulers.mainThread()).thenReturn(testScheduler);
userID = UUID.randomUUID().toString();
}
@After
public void clearMocks() {
//Mockito.framework().clearInlineMocks();
}
@Test
public void verifyUserData_callsPollResult_returnsResponse() {
// Input
String docStr = "iVBORw0KGgoAAAANSUhEUgAAAJ4AAACeCAYAAADDhbN7AA.....";
// Output
JsonObject verifyResponse = new JsonObject();
verifyResponse.addProperty("status", "Response created");
doReturn(Single.just(verifyResponse)).when(userManager).verifyUserData(docStr, docStr);
// spy method call
spyUserApi.verifyUserData(docFrontArr, docFrontArr);
testScheduler.triggerActions();
// assert
verify(userManager).verifyUserData(docStr, docStr);
}
}
错误
java.lang.NullPointerException
at com.rahul.manager.UserApi.verifyUserData(UserApi.java:60)
at com.rahul.manager.UserApiTest.verifyUserData_callsPollResult_returnsResponse(UserApiTest.java:171)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
我不确定是否可以通过使用 PowerMock 监视 Singleton 类的真实实例来测试 Singleton 类的方法。
【问题讨论】:
-
尝试在prepare注解中添加UserAPI.class
-
@Sergio 我已经添加它实际上在发布时确实出现了拼写错误,现在已更正。
-
UserManager是您的课程之一吗?如果您所说的是正确的,那么verifyUserData将返回null,这要么是由于模拟的参数定义不匹配,要么因为变量userManager是null。您可能想删除与您的课程和测试无关的任何内容,然后您可以发布minimal reproducible example。 -
@second UserManger 是由相应类实现的接口,我已经模拟了相同的,还在上面的帖子中添加了所需的代码。所以这不会造成任何问题。实际上,通过 RxJava Single 方法调用 Schedulers.io() 方法时会出现 nullPointerException 错误。我不确定在调用 UserApi.getInstance() 方法后是否可以通过监视 UserApi 的真实实例来测试单例类方法。
标签: java android mockito rx-java powermock