How to test presenter method that create a new object inside it? - android

I have this method to validate Unit. I send field values and inside it method I create a new model and then return that model via interface.
public void validate(String unitNumber, Integer unitTypeId, String notes) {
if(!TextUtils.isEmpty(unitNumber)) {
Unit unit = new Unit();
unit.setUnitNumber(unitNumber);
unit.setFlatTypeId(unitTypeId);
unit.setNotes(notes);
view.displayUnitValid(unit);
} else {
view.displayUnitNotValid();
}
}
Now I want do do unit testing of this method with the following code.
#Test public void shouldValidateSinceUnitNumberIsValid() {
// Given
String unitNumber = "1";
// When
presenter.validate(unitNumber, null, null);
// Then
Mockito.verify(view).displayUnitValid(new Unit());
}
I am getting the following error message:
Argument(s) are different! Wanted:
view.displayUnitValid(
com.rwar.domain.customers.Unit#57cf54e1
);
-> at com.rwar.presentation.work_orders.AddUnitPresenterTest.shouldValidateSinceUnitNumberIsValid(AddUnitPresenterTest.java:73)
Obvisouly there is a problem since I am creating a new Unit instance here:
Mockito.verify(view).displayUnitValid(new Unit());
And inside validate() method I create another instance of Unit.
How I can fix this?

Pass the same arguments to your verifying method, e.g.
Unit expected = new Unit()
unit.setUnitNumber(unitNumber);
unit.setFlatTypeId(unitTypeId);
unit.setNotes(notes);
Mockito.verify(view).displayUnitValid(expected);
You'll likely have to override your Unit classes equals/hashcode method such that they compare their contents and not the instance itself.

Here is the working solution that might be useful to someone else:
#Test public void shouldValidateSinceUnitNumberIsValid() {
// Given
String unitNumber = "1";
// When
presenter.validate(unitNumber, null, null);
// Then use ArgumentCaptor to get unit value from newly created object inside validate() method
ArgumentCaptor<Unit> argument = ArgumentCaptor.forClass(Unit.class);
Mockito.verify(view).displayUnitValid(argument.capture());
// Compare captured argument of Unit number with local unitNumber
assertEquals(argument.getValue().getUnitNumber(), unitNumber);
}

In case you want to do this in Kotlin, you can use Mockito check function to make asserts over the Unit instance that is passed as an argument of displayUnitValid(). Something like this:
Mockito.verify(view).displayUnitValid(Mockito.check { unit ->
assertEquals(unitNumber, unit.getUnitNumber)
});
More info about check here

Related

Android MVVM: databinding value is not set from MediatorLiveData in particular situation

When setting a value to MediatorLiveData that reacts to a source added in the constructor of a viewModel or activity onCreate observer in the ViewModel , like this for example:
showingMethodLiveData.addSource(stateChangeLiveData) {
when (it) {
ConfigurationState.CURRENT -> showingMethodLiveData.value = commMethod[it]
ConfigurationState.PENDING -> showingMethodLiveData.value = commMethod[it]
}
}
The value isn't set to the observing view, although the set method is called.
I can work around this by either adding the source in onStart (which creates other problems of registering observer more than once), or using postValue instead of setValue.
The debug of setValue method leads me to following code, where there is an interesting comment that tells the story, the method returns without setting the value to the binded view.
in androidx.databinding package of lifecycle dependency:
class ViewDataBinding:
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (mInLiveDataRegisterObserver) {
// We're in LiveData registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
}
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind();
}
}
The value is not set afterwards either, but only when the mediatorlivedata is invoked again by change in it's source.
Why this situation occurs?
Thank you for the help
PS
I think it may be an android library bug
The use of Mediatorlivedata is to compare two values and then provide a result.
If you want to change the value of a variable, you can simply use MutableLiveData and to assign a new value, write variableName.value = newValue
Should be even easier to achieve like this:
val showingMethodLiveData = Transformations.map(stateChangeLiveData) { commMethod[it] }

How to get non null result from getValue() of mapped LiveData without calling observe()?

I'm using Transformations.map method to get a new LiveData based on original one. While getValue method of the original returns always a correct value, the same accessor of the mapped on returns null.
How can I solve or workaround this issue in order to test the class exposing LiveData without calling observe on it?
Here is the code explaining this problem:
public class LiveDataTest {
#Rule
public TestRule rule = new InstantTaskExecutorRule();
#Test
public void mapTest() {
final MutableLiveData<String> original = new MutableLiveData<>();
final LiveData<String> mapped = Transformations.map(original, input -> "Mapped: " + input);
System.out.println(original.getValue()); // null - OK
System.out.println(mapped.getValue()); // null - OK
original.setValue("Hello, World!");
System.out.println(original.getValue()); // "Hello, World!" - OK
System.out.println(mapped.getValue()); // null - Should be "Mapped: Hello, World!"
}
}
From docs https://developer.android.com/reference/android/arch/lifecycle/Transformations:
The transformations aren't calculated unless an observer is observing
the returned LiveData object.
So mapped has to be observed first.
To editted post: just call mapped.observeForever(); passing in empty observer, before trying to get mapped value.

How to mock the same method multiple times use mockito

I have a method that needs to be called multiple times, and I can return the same result in the test case, I invoke when use for loop, but is there more simple way to do this?
val ONE_DAY_FORMAT: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
val tempCalendar = Calendar.getInstance()
for (i in (0..15)) {
`when`(accountingDao.sumOfDay(ONE_DAY_FORMAT.format(tempCalendar.time)))
.thenReturn(100.0f)
tempCalendar.add(Calendar.DAY_OF_YEAR, -1)
}
Normally when the set-up is more complicated, the doAnswer strategy would be used:
Mockito.doAnswer(new Answer<Float>() {
#Override
public Float answer(InvocationOnMock invocation) throws Throwable {
String argument = (String)invocation.getArgument(0);
if(supportedDates.contains(argument)){
return 100.00f;
}else{
return null;
}
}
}).when(accountingDao.sumOfDay(any(String.class)));
So you basically catch the input param and then decide based on its value what should be returned dynamically.
Instead of mocking the same method, mock the method once and call the verify method by passing the optional Verification mode parameter. For example if you want that some method of your mocked class get called twice, you can make a verify statement like this
verify(mockedClass, Mockito.times(2)).someMethod();
This will test if someMethod() is getting called twice.
Move your "when ..." statement out of your loop:
when`(accountingDao.sumOfDay(any()).thenReturn(100.0f)

Android rxJava - Null object wrapped in Observable

I have a use case that is as simple as this: Look for a Book in the Remote source, if it doesn't exist, create it.
So this is my approach that used to work with rxJava1:
public void handleBook(int id) {
getBookById(id)
.flatMap(new Func1<Book, Observable<Book>> {
#Override
public Observable<Book> call(Book book) {
if(book != null) // it exists
...
else // it's null - it doesn't exist ...
}
}
}
public Observable<Book> getBookById(int id) {
Book book = remoteSource.book(id)
return Observable.just(book)
}
In this case if the object book is null, the Observable.just call throws an exception. It's explicitly checking against null values.
Back for rxJava 1 I could pass a null as value to Observable.just (or other accesoars), then in the map or flatMap operators I'd check if the value I get is null (meaning result does not exist) or not (meaning I got some result).
With this limitation it seems that I cannot do this check anymore. I tried returning Observable.empty() in case the book object is null but then the whole thread would complete and finish when the returned value is Observable.empty().
How can I check in a rx-chain of execution if something I need is there and branch the chain of execution afterwards?
Instead of using Observable<Book> as a return type use Single<Book>. Single type emits either an object or an error
NOTE: Right now I don't have an IDE so the code will probably have some compiler fails. I assume you'll fix it easily
public Single<Book> getBookById(int id) {
return Single.create((e) => {
Book book = remoteSource.book(id)
if (book != null)
e.emit(book);
else
e.fail();
}
}
public void handleBook(int id) {
getBookById(id)
.onErrorResume(e => createBook(...))
...
}
Like a said earlier I'm not sure about the exact code but you can read it as if it was pseudo-code

manage null boolean with RxJava2 PublishSubject

I'm implementing the MVP design pattern. My presenter receives the new values from the view. I want to manage the state of a next button by automatically check if everything is valid when values are updated on the view.
In my form I have an optional part which is displayed only if the user select the correct option.
In this optional part I have a binary question. If the part is not displayed I need to set the value of the question to null on the Presenter side.
For example, the user select the option and the optional part is displayed. The user select the answer. Then the user change the option and the optional part is hidden. In that case I need to set the answer to the optional question to null, for the answer to not be already selected if the user display the optional part again.
To do so, I call a method on the Presenter with a null value instead of true/false.
Here is the code:
private final PublishSubject<Boolean> mObsOptionalAnswer = PublishSubject.create();
public MyPresenter(){
// Combine all the values together to enable/disable the next button
Observable.combineLatest(
// ... other fields
// I need this to return false if the optional part is
// displayed but nothing is selected
mObsOptionalAnswer.map(this::isValid),
(...) -> ...
).subscrible(enable ->{
mView.enableBtn(enable);
});
}
public void myFunction(Boolean isSomething){
// ... some code
mObsOptionalAnswer.onNext(isSomething);
}
private boolean isValid(Boolean value){
return value != null;
}
The problem is, since RxJava 2, null values are not allowed in the onNext() method.
So, how am I supposed to manage that?
If you want to be able to send a null value, you can use a wrapper. In this configuration, you send the wrapper, which isn't null even if the value itself is.
public class BooleanWrapper {
public final Boolean value;
public BooleanWrapper(Boolean value) {
this.value = value;
}
}
Your PublishSubject<Boolean> becomes a PublishSubject<BooleanWrapper> and you just have to create the wrapper and de-reference your Boolean when needed :
mObsOptionalAnswer.onNext(new BooleanWrapper(isSomething));
and
mObsOptionalAnswer.map(wrapper -> this.isValid(wrapper.value))
If you need to do that more than once in your code, you can create a generic wrapper (as described by this tutorial) :
public class Optional<M> {
private final M optional;
public Optional(#Nullable M optional) {
this.optional = optional;
}
public boolean isEmpty() {
return this.optional == null;
}
public M get() {
return optional;
}
}
you could use a constante Boolean object
public static final Boolean RESET_VALUE = new Boolean(false);
and you can emit this instead of emitting null. The receiver would have to check against this instance and behaving accordingly. Eg.
.subscrible(enable ->{
if (enable != RESET_VALUE) {
mView.enableBtn(enable);
}
});

Categories

Resources