I have state capturing test which needs to test something like this:
init{
loadValues();
}
fun loadValues(){
setStateToLoadingHere();
try{
val result: List<Anything> = getValues();
setStateToSuccessHere(result);
}catch(Exception e){
setStateToErrorHere();
}
}
I want to ignore this getValues() method call to be able to test the loading state only. Is there a way where I can ignore this whole try catch? If I don't do that I'll get NPE inside my test.
Try to use spy().
val spy = spy(YouClass())
doNothing().when(spy).setStateToSuccessHere(any())
doNothing().when(spy).setStateToErrorHere(any())
spy.loadValues()
Related
First time testing Rx, so please give some advice.
Need to test observable chain in model that gives as a result 21 data objects:
public Observable<BaseUnit> exec(int inputNumber) {
return getListObservable(inputNumber).subscribeOn(Schedulers.computation())
.flatMap(resultList -> getOperationsObservable()
.flatMap(operationElem -> getResultListObservable(resultList)
.flatMap(listElem -> Observable.just(listElem)
.flatMap(__ -> calculate(operationElem, listElem)
.subscribeOn(Schedulers.computation())))));
}
Here is some way I tried.
First try (here I have problems with get results and check them separately, because results of calculations inside each object unknown):
TestObserver<BaseUnit> observer = repository.exec(1000000)
.test()
.awaitDone(5, TimeUnit.SECONDS)
.assertNoErrors()
.assertValueCount(21)
.assertComplete();
Second way:
ArrayList<BaseUnit> result = new ArrayList<>();
Observable<BaseUnit> observable = repository.exec(1000000);
observable.subscribe(result::add);
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
assertNotNull(result);
assertEquals(21, result.size());
// here may be some loop of "assers"
Is that correct to test like I pointed it second way with time delay and loop?
How to check correctly that as a result it gives 21 object and check with condition that some property of data class is greater than zero? Is there any other cheks that need to be performed?
Also have one general question: need to test mvp presenter that use this model method and as a result display recieved values. How to do this using only JUnit4?
I have Boolean booleanCheckAvailabilityData to check availability data in my activity to create add/remove favorite. then i create
dataFavoriteMovieById = favoriteMovieViewModel.getAllFavoriteMovieById(idMovie);
to get data by id. so i make conditional statement to check avaiability data then put the result to boolean and i use the boolean later to add or remove the favorite.
if (dataFavoriteMovieById == null) {
booleanCheckAvailabilityData = false;
} else {
booleanCheckAvailabilityData = true;
}
In the first run, it work. my dataFavoriteMovieById is null
But, after i add or remove favorite. it always always contains data (RoomTrackingLiveData).
How can i solve this...
my code link : https://github.com/komangss/Submission-Menjadi-Android-Developer-Expert/blob/master/app/src/main/java/com/dicoding/submissionmade2_1/activity/DetailMovieActivity.java
I played with your app (thanks for providing a github link) and here are my results.
Latest app version
Your latest implementation doesn't produce an NPE anymore since you use getAllFavoriteMovieById in a more consistent way. You no longer initialize a LiveData instance in FavoriteMovieRepository by yourself but delegate it to Room to do it for you. So, you won't get an NPE since Room will always create a list to return results. If there're no items, it will return an empty list. So, you can safely remove a try/catch here:
try {
favoriteMovieViewModel.getAllFavoriteMovieById(idMovie).observe(this, new Observer<List<FavoriteMovie>>() {
#Override
public void onChanged(List<FavoriteMovie> favoriteMovies) {
booleanCheckAvailabilityData = favoriteMovies.size() != 0;
}
});
} catch (NullPointerException e) {
Log.d("ini bug nya", e.getMessage());
}
Original app version
In addition to what #Paul Ost said about how favoriteMovieViewModel should be used properly (by listening to it, not using it directly), I will explain why you actually had an NPE.
In that version, you were running into a NullPointerException because you returned the favoriteMovieById LiveData before it was actually initialized in your GetFavoriteMovieByIdAsyncTask.
So, here what was happening in detail. First, once your DetailMovieActivity had been created, favoriteMovieViewModel called getAllFavoriteMovieById() as below:
DetailMovieActivity.java
...
favoriteMovieViewModel = ViewModelProviders.of(this).get(FavoriteMovieViewModel.class);
dataFavoriteMovieById = favoriteMovieViewModel.getAllFavoriteMovieById(idMovie);
...
FavoriteMovieViewModel.java
FavoriteMovieViewModel instance, in turn, delegated the call to FavoriteMovieRepository instance as below:
public LiveData<List<FavoriteMovie>> getAllFavoriteMovieById(int idMovie) {
return repository.getFavoriteMovieById(idMovie);
}
FavoriteMovieRepository.java
Finally, getFavoriteMovieById started a GetFavoriteMovieByIdAsyncTask and returned favoriteMovieById:
public LiveData<List<FavoriteMovie>> getFavoriteMovieById(int id_movie) {
new GetFavoriteMovieByIdAsyncTask(favoriteMovieDao).execute(id_movie);
return favoriteMovieById;
}
But that's wrong, since your favoriteMovieById was set to null by default, and so on the first run, you were always getting it.
Your AsyncTask was eventually setting a non-null value, but it was too late:
...
private static class GetFavoriteMovieByIdAsyncTask extends AsyncTask<Integer, Void, Void> {
...
#Override
protected Void doInBackground(Integer... integers) {
FavoriteMovieRepository.favoriteMovieById = favoriteMovieDao.getFavoriteMovieById(integers[0]);
return null;
}
}
...
From what I can see in your code - getAllFavoriteMovieById works as expected. The thing is - you are using LiveData as a return type of getAllFavoriteMovieById thus it returns not the value itself but a LiveData wrapper. But if you will try to observe this LiveData object you will(presumably since I haven't seen relevant code) receive null instead of favourite value. The only correct place to assign value to your booleanCheckAvailabilityData inside this observer(depending on your DAO code of course).
favouriteMovieViewModel.getAllFavoriteMovieById().observe(this, Observer { data ->
if (data == null) {
booleanCheckAvailabilityData = false;
} else {
booleanCheckAvailabilityData = true;
}
})
Something like that(once again it depends on your DAO code and getAllFavoriteMovieById implementation)
Hope it helps.
In ROOM Try Deleting the old TABLE before inserting the new data. In that case the old data will be deleted as we are deleting the old data
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
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)
We currently have an Android application that we are testing with Espresso. One of the features we want to test is selecting a picture/image from the local image gallery. We can get all the way to bringing up the Gallery view, but then cannot select from Recent, Downloads, Gallery in the resulting window. A snippet as to how we got as far as we did is included below.
public void testShouldBeAbleToSelectPhotoFromGallery() {
getActivity();
// given
onView(withId(launch_gallery_button)).perform(click());
onView(withText("Gallery")).perform(click()); // this is a button in our app
// then we get stuck :(
}
Thanks!
This is not possible with with either Robotium or Espresso, as they only work on Activities of the app under test.
To write integration tests that work across different apps and Android built-in apps, you can use the UiAutomator framework provided by Google.
Basically you would analyse the gallery app in the uiautomatorview to learn how to select the ui elements your test case needs and then act on them, not unlike Espresso.
If you want to test that functionality in your app you should use the intent mocking functionality in Espresso.
Espresso tests should not leave your app in the first place.
Instead you catch the intent you use to open the gallery app and return a result back to your app.
During the test you will stay in your app, you will get a result immediately.
To do this check the intending and intended api's of Espresso.
Here is a tutorial by Pengj to get you acquainted with intent mocking. The tutorial mentions Mockito but you can perfectly use this without it.
The best and proper way is to use Espresso Intents. So you need to add the dependency in your app's build.gradle
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
In my case I was openning the gallery from a button within my app, then the code for the test and the addition of the intending and intended api's of Espresso as follows:
#Test
fun photos_CreationGalleryClickUI() {
savePickedImage()
val imgGalleryResult = createImageGallerySetResultStub()
intending(hasAction(Intent.ACTION_CHOOSER)).respondWith(imgGalleryResult)
onView(withId(R.id.photos_button_gallery)).perform(click())
onView(withId(R.id.photos_bigimage_viewer)).check(matches(hasImageSet()))
}
Here the matcher for intending is the key when the gallery needs to be open and avoid to manually pick an image:
hasAction(Intent.ACTION_CHOOSER)
I' using two helpers:
savePickedImage() to mock an image from the gallery
private fun savePickedImage() {
val bm = BitmapFactory.decodeResource(mActivityTestRule.activity.resources, R.mipmap.ic_launcher)
assertTrue(bm != null)
val dir = mActivityTestRule.activity.externalCacheDir
val file = File(dir?.path, "myImageResult.jpeg")
System.out.println(file.absolutePath)
val outStream: FileOutputStream?
try {
outStream = FileOutputStream(file)
bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream)
outStream.flush()
outStream.close()
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
And createImageGallerySetResultStub to stubbing the result after "picking" the image. Here putting the result as an array of parcelables was key, without it, the never result was never recognized:
private fun createImageGallerySetResultStub(): Instrumentation.ActivityResult {
val bundle = Bundle()
val parcels = ArrayList<Parcelable>()
val resultData = Intent()
val dir = mActivityTestRule.activity.externalCacheDir
val file = File(dir?.path, "myImageResult.jpeg")
val uri = Uri.fromFile(file)
val myParcelable = uri as Parcelable
parcels.add(myParcelable)
bundle.putParcelableArrayList(Intent.EXTRA_STREAM, parcels)
resultData.putExtras(bundle)
return Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
}
hasImageSet() as a matcher helper that checks if the imageView has or not a drawable:
return item.getDrawable() == null
NOTE: Remember to set the grant rule to avoid problems with the permissions and to define your test rule as an IntentTestRule (which extends from ActivityTestRule already)
#get:Rule
var mActivityTestRule = IntentsTestRule(AuctionCreationActivity::class.java)
#get:Rule
var mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
The dependency:
androidTestImplementation "androidx.test:rules:$testRules"