Muszę wykonać testy jednostkowe metod klasy Singleton, która wewnętrznie korzysta z RxJava Singles i używa struktury testowej PowerMock do mockowania statycznej klasy i metod. Próbowałem różnych metod, aby mockować metody Schedulers.io () i AndroidSchedulers.mainThread (), ale to nie działa. Otrzymuję błąd java.lang.NullPointerException w wierszu .subscribeOn (Schedulers.io ()) w metodzie UserApi.verifyUserData () .
Singleton Class UserApi (testowana klasa)
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...
}
}
Klasa i interfejs 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...
}
}
Test jednostkowy
@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);
}
}
Błąd
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)
Nie jestem pewien, czy mogę przetestować metody klasy Singleton, szpiegując rzeczywiste wystąpienie klasy Singleton za pomocą PowerMock.
2 odpowiedzi
Testowanie kodu jest złożone, ponieważ nie można go testować ani rozszerzać. Zawiera wszędzie zakodowane zależności (np. identyfikator użytkownika, program obsługi, kilka singli).
Jeśli zdecydujesz się użyć innego podejścia do generowania identyfikatorów lub innego modułu obsługi, nie będziesz w stanie tego zrobić bez przepisania całej klasy.
Zamiast sztywno kodować zależności, poproś o nie w konstruktorze (dla zależności obowiązkowych) lub setters (dla opcjonalnych).
Dzięki temu Twój kod będzie rozszerzalny i testowalny. Po wykonaniu tej czynności zobaczysz, że twoja klasa zawiera kilka obowiązków, po przeniesieniu ich do osobnych klas uzyskasz znacznie lepszy obraz :-)
Na przykład:
public UserApi(String userId, Handler handle) {
this.userId = userId;
this.handler = handler;
}
Schedulers.io()
jest metodą statyczną, więc musisz użyć mockStatic
(co zrobiłeś) i odpowiednio zdefiniować powiązany mock.
Trochę przestawiłem Twoją metodę setup
, aby poprawić czytelność i naprawiłem błąd. Nie potrzebujesz instancji Schedulers
(zmienna, którą nazwałeś schedulers
).
Prawdopodobnie prosta literówka, którą zrobiłeś, ponieważ zrobiłeś właściwą rzecz dla Base64
i AndroidSchedulers
.
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
handler = new Handler();
// mock for instance of `IdvManager`
PowerMockito.suppress(constructor(IdvManager.class));
idvManager = PowerMockito.mock(IdvManager.class);
when(idvManager.getUserManager()).thenReturn(userManager);
// mock for `IdvManager` class
PowerMockito.mockStatic(IdvManager.class);
PowerMockito.when(IdvManager.getInstance()).thenReturn(idvManager);
// mock for `Schedulers` class
PowerMockito.mockStatic(Schedulers.class);
when(Schedulers.io()).thenReturn(testScheduler);
// spy for instance of `UserApi`
spyUserApi = PowerMockito.spy(UserApi.getInstance());
// mock for `Base64` class
PowerMockito.mockStatic(Base64.class);
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]);
}
});
// mock for `AndroidSchedulers` class
PowerMockito.mockStatic(AndroidSchedulers.class);
when(AndroidSchedulers.mainThread()).thenReturn(testScheduler);
userID = UUID.randomUUID().toString();
}
Jednak w NPE
brakuje części, która faktycznie wskazuje na jego awarię, rozważ dodanie jej, jeśli to nie rozwiąże problemu.
Podobne pytania
Nowe pytania
java
Java to język programowania wysokiego poziomu. Użyj tego tagu, jeśli masz problemy z używaniem lub zrozumieniem samego języka. Ten tag jest rzadko używany samodzielnie i jest najczęściej używany w połączeniu z [spring], [spring-boot], [jakarta-ee], [android], [javafx], [hadoop], [gradle] i [maven].
UserManager
jest jedną z Twoich zajęć? Jeśli to, co mówisz, jest poprawne, toverifyUserData
zwrócinull
, co jest albo z powodu niezgodnych parametrów zdefiniowanych dla makiety, albo dlatego, że zmiennauserManager
tonull
. Możesz usunąć z zajęć i testu wszystko, co nie jest powiązane, a następnie opublikować minimalny powtarzalny przykład.