How to unit test nullability of array list in Android? - android

I have a method:
public void clearLogItems() {
if (logItems != null) {
this.logItems.clear();
this.logItems = null;
}
}
and I would like to write a unit test for it.. If my logic is right, I should check 3 things here:
Don't call logItems.clear() if logItems is null
Call logItems.clear() if logItems is not null
Set logItems to null after calling logItems.clear()
Now I'm not sure how to Mock logItems to null, to verify clear() will be called 0 times..
#RunWith(MockitoJUnitRunner.class)
public class LogItemDataTest {
LogItemData SUT;
#Mock
List<LogItemData> logItems;
#Before
public void setup() throws Exception {
SUT = new LogItemData();
}
#Test
public void clearLogItems() {
// Arrange
// Act
SUT.clearLogItems();
// Assert
Mockito.verify(logItems, times(1)).clear();
}
}
With this code, I tried to cover the case where logItems is not null and when logItems.clear() is called 1 time, but when I run it, it says there were 0 interactions with this mock...
How do I mock the logItems list to cover the null case?

The LogItemData object SUT does not know about the logItems. You have to create SUT so that it knows about your logItems. How that works depends on your implementation of LogItemData E.g. if it has a constructor it may look like this
#Before
public void setup() throws Exception {
SUT = new LogItemData(logItems);
}

Related

Create test case for the method who modify its input and pass to mock object

I am using Mockito for writing unit test case in Android. I am stuck into one method where I am modify Object and pass it to mocked method, Not able to understand how to write unit test case for this
Class LocationViewModel{
private LocationInteractor locationInteractor;
LocationViewModel (LocationInteractor locationInteractor){
this.locationInteractor =locationInteractor;
}
#Override
public Single<List<String>> getRecentLocations( LocationViewType locationViewType) {
return locationInteractor.getUpdatedRecentLocation(getRecentLocationFilter(locationViewType),locationViewType);
}
private Map<String, String[]> getRecentLocationFilter(LocationViewType locationViewType) {
LocationFilter locationfilter = new LocationFilter();
if (locationViewType == LocationViewType.DEFAULT_LOCATIONS) {
return locationFilter.getRecentDefaultLocationFilter();
} else if (locationViewType == SETTING_LOCATIONS) {
return locationFilter.getRecentSettingLocationFilter();
} else if (locationViewType == LocationViewType.INVENTORY_LOCATION) {
return locationFilter.getRecentSettingLocationFilter();
} else {
return locationFilter.getRecentCurrentLocationFilter();
}
}
}
Class LocationViewModelTest{
#Mock private LocationInteractorContract mockLocationInteractor;
private LocationViewModelContract locationViewModel;
#Before
public void setUp() {
initMocks(this);
locationViewModel = new LocationViewModel(mockLocationInteractor)
}
#Test
public void getRecentLocationsList_check_for_Null() {
when(mockLocationInteractor.getUpdatedRecentLocation(anyMap(),LocationViewType.SETTING_LOCATIONS)) ......Line 1
.thenReturn(Single.error(NullPointerException::new));
locationViewModel
.getRecentLocations(LocationViewType.SETTING_LOCATIONS)
.test()
.assertFailure(NullPointerException.class);
}
}
When I use anyMap() in Line no 1 it throws - org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
When I use new HashMap<>() in Line no 1 it throws NullPointerException
Want to write test case for method - getRecentLocations where getRecentLocationFilter is private method
For the InvalidUseOfMatchersException, the reason is probably that you have to use either all values or all matchers. For example:
when(mockLocationInteractor.getUpdatedRecentLocation(anyMap(), any())

Actual invocation has different arguments Unit Presenter

I create a unit test for my Presenter. My Presenter implements Listener callback if successfully load data from API (use Interactor):
PresenterTest.java
public class MainContactPresenterTest {
#Mock LoadContactInteractor loadContactInteractor;
#Mock ApiService apiService;
#Mock LoadContactView loadContactView;
#Mock ContactRepository contactRepository;
#Mock LoadContactInteractor.OnLoadDataFinishedListener listener;
#InjectMocks MainContactPresenterImpl presenter;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void getContactLists() {
// given
// when
presenter.fetchRemoteContacts();
// then
Mockito.verify(loadContactInteractor).onLoadData(listener);
}
}
Here is my Presenter:
public class MainContactPresenterImpl implements MainContactPresenter,
LoadContactInteractor.OnLoadDataFinishedListener {
private LoadContactView loadContactView;
private LoadContactInteractor loadContactInteractor;
private ContactRepository contactRepository;
#Inject
public MainContactPresenterImpl(LoadContactInteractor loadContactInteractor,
#NonNull LoadContactView loadContactView,
ContactRepository contactRepository) {
this.loadContactView = loadContactView;
this.loadContactInteractor = loadContactInteractor;
this.contactRepository = contactRepository;
}
#Override
public void onSuccessLoad(List<Contact> contacts) {
loadContactView.saveDataToLocalStorage(contacts);
}
#Override
public void onErrorLoad() {
loadContactView.dismissProgress();
loadContactView.showErrorMessage();
}
#Override
public void preCheckCacheData() {
if (contactRepository.getContactCount() == 0) {
// Load contacts from Server
fetchRemoteContacts();
} else {
fetchLocalContacts();
}
}
#Override
public void fetchRemoteContacts() {
loadContactView.showProgress();
loadContactInteractor.onLoadData(this);
}
}
But when I ran test, I got the mocking parameter in verify not match.
I got my presenter that have to be an argument. Not the listener.
Argument(s) are different! Wanted:
loadContactInteractor.onLoadData(
listener
);
Actual invocation has different arguments:
loadContactInteractor.onLoadData(
fanjavaid.gojek.com.contacts.presenter.MainContactPresenterImpl#1757cd72
);
How to handle that? Thank you
You are creating a mock...
#Mock LoadContactInteractor.OnLoadDataFinishedListener listener;
...and then you don't use it ever again and act suprised when verify tells you, that it wasn't actually used. Why? Of course it wasn't used, since you never use it anywhere, so how should your classes know to use that mock object?
Your MainContactPresenterImpl does not use an OnLoadDataFinishedListener as an external dependency (then your could perhaps inject it via #InjectMocks), it is itself such a listener and thus mocking another listener makes no sense here.
In other words, MainContactPresenterImpl has no OnLoadDataFinishedListener field, so Mockito is of course not capable of injecting something in this non-existing field. For something like this to work, you would need to add such a field and then use the content of that field when calling your onLoadData method.
The only invocation of your method is here...
loadContactInteractor.onLoadData(this);
And what is this in that context? It's the MainContactPresenterImpl object that contains the method, in other words, your presenter.
So, what will work is...
Mockito.verify(loadContactInteractor).onLoadData(presenter);

Unit test Android, getString from resource

I am trying to do an unit test for an android app and I need to get a string from res.string resources. The class that I want to test is a POJO class. I am doing the app in two languages, due to this, I need to get a string from resource. The problem is that I cannot get the context or the activity, is possible? I know that with Instrumentation test I can do it, but I need to test some functions (white box test) before to do the instrumentation test (black box test).
This is the function that I have to test:
public void setDiaByText(String textView) {
getll_diaSeleccionado().clear();
if (textView.contains(context.getResources().getString(R.string.sInicialLunes))) {
getll_diaSeleccionado().add(0);
getIsSelectedArray()[0] = true;
getI_idiaSeleccionado()[0] =1;
} else
{
getIsSelectedArray()[0] = false;
getI_idiaSeleccionado()[0] =0;
}
}
And this is the test:
#Test
public void setDiaByTextView() {
String texto = "L,M,X,J,V,S,D";
alertaPOJO.setDiaByText(texto);
assertEquals(alertaPOJO.getIsSelectedArray()[0], true);
assertEquals(alertaPOJO.getI_idiaSeleccionado()[0], 1);
}
It crash when try to do context.getResources().getString(R.string.sInicialLunes))
If I put 'Mon' instead of context.getResources().getString(R.string.sInicialLunes)) or 'L' it work perfectly so, is possible to get the context or the activity in order to access to resource folder?
I am testing with Mockito and the setUp function is:
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = Mockito.mock(Alerta.class);
Mockito.when(mContext.getApplicationContext()).thenReturn(mContext);
alertaPOJO = new AlertaPOJO();
}
Thanks
If you are using Context only for obtaining String resource, I would go by mocking only getResources().getString() part like this (see JUnit4 notation):
#RunWith(MockitoJUnitRunner.class)
public class AlertaPOJOTest {
#Mock
Context mMockContext;
#Test
public void setDiaByTextView() {
String texto = "L,M,X,J,V,S,D";
when(mMockContext.getString(R.string.sInicialLunes))
.thenReturn(INITIAL_LUNES);
alertaPOJO.setDiaByText(texto);
assertEquals(alertaPOJO.getIsSelectedArray()[0], true);
assertEquals(alertaPOJO.getI_idiaSeleccionado()[0], 1);
}
}
There are many reasons to stay with JVM tests, most important one, they are running quicker.
Untested: would it work to use the below, and probably targetContext?
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
You don't have a real android Context while you are using JVM unit test. For your case, maybe you can try Android Instrumentation Test, typically it is implemented in the "androidTest" directory of your project.
If you use MockK it's the same.
#RunWith(AndroidJUnit4::class)
class YourClassUnitTest : TestCase() {
#MockK
private lateinit var resources: Resources
#Before
public override fun setUp() {
MockKAnnotations.init(this)
}
#Test
fun test() {
every {
resources.getQuantityString(R.plurals.age, YEARS, YEARS)
} returns AGE
every {
resources.getString(
R.string.surname,
SURNAME
)
} returns TITLE
// Assume you test this method that returns data class
// (fields are calculated with getQuantityString and getString)
val data = getData(YEARS, SURNAME)
assertEquals(AGE, data.age)
assertEquals(TITLE, data.title)
}
companion object {
const val YEARS = 10
const val AGE = "$YEARS years"
const val SURNAME = "Johns"
const val TITLE = "Mr. $SURNAME"
}
}
See also Skip a parameter in MockK unit test, Kotlin to get a result of string resources for any data.

UntTest - Mock RxJava object in UseCase Object

I want to test UseCase object, in this specific case there is a LoginUseCase, which looks like this:
public class LoginUseCase implements RxUseCase<AuthResponse, AuthCredentials> {
ApiManager mApiManager;
public LoginUseCase(ApiManager apiManager) {
mApiManager =apiManager;
}
#Override
public Observable<AuthResponse> execute(final AuthCredentials authCredentials) {
return Observable.just(1)
.delay(750, TimeUnit.MILLISECONDS)
.flatMap(l -> mApiManager.login(authCredentials.getLogin(), authCredentials.getPassword()));
}
}
I wrote simple test:
#RunWith(MockitoJUnitRunner.class)
public class LoginUseCaseTest {
private LoginUseCase mLoginUseCase;
#Mock ApiManager mApiManager;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mLoginUseCase = new LoginUseCase(mApiManager);
}
#Test
public void testShouldThrowsError() throws Exception {
TestSubscriber<AuthResponse> testSubscriber = new TestSubscriber<>();
doReturn(Observable.error(new Throwable())).when(mApiManager).login("", "");
mLoginUseCase
.execute(new AuthCredentials("", ""))
.subscribe(testSubscriber);
testSubscriber.assertNoErrors();
}
}
But this test always passes and I don't know how mock error observable in this case.
EDIT: I've chaged testShouldThrowsError() according to SkinnyJ, but test still passes, any sugestions?
You need to call awaitTerminalEvent() on your test subscriber before assertions.
Because now, you schedule a delay to be run on Schedulers.computation and your test method successfully completes before completion of observable.
Alternative approach would be to pass scheduler as argument to execute method, or store scheduler in your usecase. This way, during test you can pass Schedulers.immediate() and your test will run on current thread (which will block execution for specified delay).
And last approach is to call toBlocking() on observable, but I think that passing scheduler is preferred choice. And there is no way to add this operator to your current observable.

Mockito and callback returning "Argument(s) are different!"

I'm trying to use mockito on android. I want to use it with some callback.
Here my test :
public class LoginPresenterTest {
private User mUser = new User();
#Mock
private UsersRepository mUsersRepository;
#Mock
private LoginContract.View mLoginView;
/**
* {#link ArgumentCaptor} is a powerful Mockito API to capture argument values and use them to
* perform further actions or assertions on them.
*/
#Captor
private ArgumentCaptor<LoginUserCallback> mLoadLoginUserCallbackCaptor;
private LoginPresenter mLoginPresenter;
#Before
public void setupNotesPresenter() {
// Mockito has a very convenient way to inject mocks by using the #Mock annotation. To
// inject the mocks in the test the initMocks method needs to be called.
MockitoAnnotations.initMocks(this);
// Get a reference to the class under test
mLoginPresenter = new LoginPresenter(mUsersRepository, mLoginView);
// fixtures
mUser.setFirstName("Von");
mUser.setLastName("Miller");
mUser.setUsername("von.miller#broncos.us");
mUser.setPassword("Broncos50superBowlWinners");
}
#Test
public void onLoginFail_ShowFail() {
// When try to login
mLoginPresenter.login("von.miller#broncos.us", "notGoodPassword");
// Callback is captured and invoked with stubbed user
verify(mUsersRepository).login(eq(new User()), mLoadLoginUserCallbackCaptor.capture());
mLoadLoginUserCallbackCaptor.getValue().onLoginComplete(eq(mUser));
// The login progress is show
verify(mLoginView).showLoginFailed(anyString());
}
But I got this error :
Argument(s) are different! Wanted:
mUsersRepository.login(
ch.example.project.Model.User#a45f686,
<Capturing argument>
);
-> at example.ch.project.Login.LoginPresenterTest.onLoginFail_ShowFail(LoginPresenterTest.java:94)
Actual invocation has different arguments:
mUsersRepository.login(
ch.example.project.Model.User#773bdcae,
ch.example.project.Login.LoginPresenter$1#1844b009
);
Maybe the issue is that the second actual argument is ch.example.project.Login.LoginPresenter$1#1844b009 ?
I followed : https://codelabs.developers.google.com/codelabs/android-testing/#5
Thank you for help =)
Edit
The method I try to test (LoginPresenter):
#Override
public void login(String email, String password) {
mLoginView.showLoginInProgress();
User user = new User();
user.setUsername(email);
user.setPassword(password);
mUsersRepository.login(user, new UsersRepository.LoginUserCallback() {
#Override
public void onLoginComplete(User loggedUser) {
mLoginView.showLoginComplete();
}
#Override
public void onErrorAtAttempt(String message) {
mLoginView.showLoginFailed(message);
}
});
}
eq(new User())
When using eq (or not using matchers at all), Mockito compares arguments using the equals method of the instance passed in. Unless you've defined a flexible equals implementation for your User object, this is very likely to fail.
Consider using isA(User.class), which will simply verify that the object instanceof User, or any() or anyObject() to skip matching the first parameter entirely.
I am using mvp pattern with rxjava 2 and dagger 2, and was stuck on unit testing a presenter using Mockito. The code that gave me the "Argument(s) are different!” Error:
#Mock
ImageService imageService;
#Mock
MetadataResponse metadataResponse;
private String imageId = "123456789";
#Test
public void getImageMetadata() {
when(imageService.getImageMetadata(imageId)).thenReturn(Observable.just(Response.success(metadataResponse)));
presenter.getImageMetaData(imageId);
verify(view).showImageData(new ImageData()));
}
Which throws error messages such as the following:
Argument(s) are different! Wanted: Actual invocation has different
arguments: com.example.model.ImageData#5q3v861
Thanks to the answer from #Jeff Bowman, it worked after I changed this line
verify(view).showImageData(new ImageData()));
with
verify(view).showImageData(isA(ImageData.class));

Categories

Resources