Mockito is verifying the wrong method - android

I'm following MVP architecture in my app using the Nucleus library, and am trying to mock the Presenter for my Fragment. The mock and its overridden invocations work perfectly fine, but my verify calls are strange. First, here's my test:
private User mUser;
private ProfilePresenter mPresenterMock;
#Override
public void setUp() throws Exception {
super.setUp();
mUser = TestUtils.generateTestUser();
mPresenterMock = mock(ProfilePresenter.class);
ProfileFragment fragment = ProfileFragment.newInstance();
fragment.setPresenterFactory(() -> mPresenterMock);
setFragment(fragment);
}
#Test
public void testInitialValues() {
doAnswer(invocation -> {
getFragment().onUserLoaded(mUser);
return null;
}).when(mPresenterMock).loadUser(anyBoolean());
startFragment();
verify(mPresenterMock).loadUser(eq(false));
onView(withId(R.id.empty)).check(matches(not(withEffectiveVisibility(VISIBLE))));
assertEquals(mUser.getFullName(), getFragment().mToolbar.getTitle());
assertEquals(1, getFragment().mVideosRecyclerView.getChildCount());
}
I get a test failure on the verify line:
Argument(s) are different! Wanted:
profilePresenter.start(false);
Actual invocation has different arguments:
profilePresenter.start(1);
What it appears to be doing is verifying a method inside the mocked class, which shouldn't be the case if it's a mocked object. This is the loadUser method inside my Presenter which invokes start:
void loadUser(boolean fresh) {
start(fresh ? USER_FRESH : USER_CACHED);
}
Where start takes an integer parameter.
I've debugged the test and have verified that the Fragment is indeed using the mocked Presenter as expected. I also put a breakpoint inside my doAnswer to verify it is being triggered correctly, and it is.
So what could cause Mockito to verify a method invocation inside of the method I'm trying to verify in the mocked class? I'm using version 1.10.19.

Related

Call mock method mockito for specific Class<T>

I am wondering to test my interactor and a piece of test code is mocking the Callback interface that is implemented for different objects on BaseInteractor, that all the Interactors implements.
public interface Callback<T> {
void onSuccess(T response);
void onError(Exception exception);
}
I want to test the onSuccess method and i made this:
#Test
public void shouldGetContributorsSuccess() {
repository = mock(Repository.class);
List<Contributor> contributor = getMockContributor();
GetContributorsInteractor interactor = getFilterReports();
GetContributorsInteractor.Callback callback = mock(GetContributorsInteractor.Callback.class);
interactor.execute(contributor.get(0).getUserName(), "Hello_World", callback);
verify(callback).onSuccess(contributor);
}
when I want to mock the Callback in this line:
GetContributorsInteractor.Callback callback = mock(GetContributorsInteractor.Callback.class);
after that i call verify(callback).onSuccess(contributor);
and show a warning that says Unchecked call to 'onSuccess(T)' as a member of raw type '... .BaseInteractor.Callback'.
I don't know if I should specify in the mock line the specific type as GetContributorsInteractor.Callback but throws an exception or what.
Thanks in advance!
Probably it is because I don't have used mockito before...
UPDATE
I suppress the warnings and I found a error that says:
Argument(s) are different! Wanted:
callback.onSuccess(
[com.example.x.iphonedroid.domain.entities.Contributor#2f686d1f]
);
-> at com.example.x.iphonedroid.domain.GetContributorsInteractorTest.shouldGetContributorsSuccess(GetContributorsInteractorTest.java:61)
Actual invocation has different arguments:
callback.onSuccess(
[]
);
Probably my error becomes from other part that i don't know how to identify but I think I am giving parameters correctly.
In the last line in verify method I pass a "List< Contributor >" and it seems like it would be an empty List.

Understanding how to test RxJava Observables

I am learning how to write android unit tests. And, I am looking at examples: So, I saw something like this:
#Test
public void getPopularMoviesMakesApiCall() {
// given that the api service returns a response
1. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));
// when getPopularMovies is invoked
2. mRemoteRepository.getPopularMovies(1).subscribeWith(mMovieListTestSubscriber);
// then, verify that the api request is made and returns the expected response
3. verify(apiService).discover(SORT_BY_POPULARITY, PAGE, API_KEY);
4. mMovieListTestSubscriber.assertValue(mMovieList);
}
I tried to run it, and I noticed option 1 executes always, option 2 does too. But, if option 3 doesn't comform with the information in option 2,
it throws an error saying they aren't the same. Which means option 3 confirms option 2. If I'm wrong or there's anything to correct, please
do tell. So, I wrote something like this:
#Test
public void testBadHashException() throws Exception {
1. mRemoteRepository.getPopularMovies(1, FAKE_API_KEY).subscribeWith(mMovieListTestSubscriber);
2. mMovieListTestSubscriber.assertNoValues();
3. mMovieListTestSubscriber.assertError(HttpException.class);
}
This is what I noticed:
private List<Movie> mMovieList;
private DiscoverMoviesResponse mDiscoverMoviesResponse;
private MoviesRepository mRemoteRepository;
private TestObserver<List<Movie>> mMovieListTestSubscriber;
private TestObserver<Movie> mMovieTestSubscriber;
#Mock
MovieApiService apiService;
Those above, were declared at the top, and initialized by a Mockito #Before #annotation like this:
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mRemoteRepository = new MoviesRemoteRepository(apiService);
mMovieTestSubscriber = new TestObserver<>();
mMovieListTestSubscriber = new TestObserver<>();
mMovieList = TestDataGenerator.generateMovieList(10);
mDiscoverMoviesResponse = TestDataGenerator.generateDiscoverMoviesResponse(mMovieList);
}
Note: TestDataGenerator is a helper class for generating data. As it's done there, he got MovieList and then got another which is the main response body.
APIService: The retrofit service class.
MoviesRepository: An helper class for manipulating Observables in the service class. Which is used by the ViewModel.
The second test keeps giving me java.lang.RuntimeException: No mock defined for invocation. I don't seem to understand it yet.
Is there a specific instance where I should use when, verify, how do I test for Observable Retrofit Request Errors.
If it's saying no Mock data, but then Mock data has been generated when this is done. Or is it supposed to be mocked differently?
mMovieList = TestDataGenerator.generateMovieList(10);
mDiscoverMoviesResponse = TestDataGenerator.generateDiscoverMoviesResponse(mMovieList);
More on my observation:
I was going through Mockito and I noticed, the first test that went through was executing because he did:
1. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));
Since the error for the second function shows java.lang.RuntimeException: No mock defined for invocation, it was stated that the method
within a class can be mocked by using when("some method").thenReturn() it's okay. I then modified my testBadHashException to look like this:
#Test
public void testBadHashException() throws Exception {
0. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));
1. mRemoteRepository.getPopularMovies(1, FAKE_API_KEY).subscribeWith(mMovieListTestSubscriber);
2. mMovieListTestSubscriber.assertNoValues();
3. mMovieListTestSubscriber.assertError(HttpException.class);
}
Instead of it throwing an exception, it threw a success.
I rewrote the error test:
#Test
public void getPopularMoviesThrowsError() {
when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(Observable.<List<Movie>>error(new TimeoutException()));
// request movies
mMoviesViewModel.discoverMovies(true);
verify(mMoviesRepository).getPopularMovies(PAGE);
// check that empty view is hidden
assertFalse(mMoviesViewModel.emptyViewShowing.get());
// check that loading view is hidden
assertFalse(mMoviesViewModel.moviesLoading.get());
// check that error view is showing
assertTrue(mMoviesViewModel.errorViewShowing.get());
}
There is a compilation error here: when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(Observable.<List<Movie>>error(new TimeoutException()));
It cannot resolve method here: Observable.<List<Movie>>error(new TimeoutException())
Writing tests in Android looks really weird compared to JavaScript. Any help on how I can learn or achieve understanding how to write unit testing would be appreciated. I just adopted
the MVVM pattern and I'm trying to write test with it. Thanks.
If you need to send an error to your Observable you can create it like this:
when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(
Observable.create(new Observable.OnSubscribe<SomeClass>() {
#Override public void call(Subscriber<SomeCladd> subscriber) {
subscriber.onError(new Exception("some message!"));
}
}));
This way you will have a Observable that returns an error so you can call
mMovieListTestSubscriber.assertError
The problem that you are having is that you using Observable.just to create your Observable... So the method onError is never going to be called, just the onNext.

Using mockito to stub a void method

mockito-core:2.7.10
I am testing the following method using mockito
#Override
public void detachView() {
mMovieListViewContract = null;
mMovieModelContract.releaseResources();
}
I am stubbing the releaseResources method which has a void return. And testing to ensure the stubbed version gets called only once.
#Override
public void releaseResources() {
if(mSubscription != null && !mSubscription.isUnsubscribed()) {
mSubscription.unsubscribe();
}
}
I have written the following test:
#Test
public void shouldReleaseModelResourcesWhenDetached() {
doNothing().when(mockMovieListModelContract).releaseResources();
movieListPresenterContract.detachView();
verify(mockMovieListModelContract, times(1)).releaseResources();
}
I want to verify that the stubbed version of mockMovieListModelContract.releaseResources() gets called just once.
The following failed the test:
verify(mockMovieListModelContract, times(1)).releaseResources();
The test failed with:
Wanted but not invoked:
movieListModelContract.releaseResources();
Actually, there were zero interactions with this mock.
So I changed to using verifyZeroInteractions(mockMovieListModelContract); which passed the test. However, the test to verify that the stubbed version gets called just once.
Kind of straight forward: the mocking framework tells you that this method wasn't invoked on that mock object.
There are only two explanations for that:
You are not really running the production code you are showing in your question (you are testing something else)
Something is wrong with your setup; and you are not "inserting" the mocked object when doing the test setup

Mockito - MissingMethodInvocationException

I am trying to test that an API call is scheduled on the right scheduler and observes on the main thread.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Observable.class, AndroidSchedulers.class})
public class ProductsPresenterTest {
private ProductsPresenter presenter;
#Before
public void setUp() throws Exception{
presenter = spy(new ProductsPresenter(mock(SoajsRxRestService.class)));
}
#Test
public void testShouldScheduleApiCall(){
Observable productsObservable = mock(Observable.class);
CatalogSearchInput catalogSearchInput = mock(CatalogSearchInput.class);
when(presenter.soajs.getProducts(catalogSearchInput)).thenReturn(productsObservable);
/* error here*/
when(productsObservable.subscribeOn(Schedulers.io())).thenReturn(productsObservable);
when(productsObservable.observeOn(AndroidSchedulers.mainThread())).thenReturn(productsObservable);
presenter.loadProducts(catalogSearchInput);
//verify if all methods in the chain are called with correct arguments
verify(presenter.soajs).getProducts(catalogSearchInput);
verify(productsObservable).subscribeOn(Schedulers.io());
verify(productsObservable).observeOn(AndroidSchedulers.mainThread());
verify(productsObservable).subscribe(Matchers.<Subscriber<Result<Catalog<SoajsProductPreview>>>>any());
}
}
The line
when(productsObservable.subscribeOn(Schedulers.io())).thenReturn(productsObservable);
throws the following exception, and I don't understand why since productObservable is a mock. Any idea or similar experience?
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
The problem is due Observable::subscribeOn being a final method, which Mockito can't mock.
One possible solution is to use Powermock:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Observable.class)
public class MockTest {
#Test
public void test() {
Observable productsObservable = PowerMockito.mock(Observable.class);
when(productsObservable.subscribeOn(null)).thenReturn(productsObservable);
productsObservable.subscribeOn(null);
verify(productsObservable).subscribeOn(null);
}
}

Android Espresso Intents test randomly fail with ``init() must be called prior to using this method``

I am working on pushing a project into espresso testing currently. I have read a bunch of documents and follow the given practises to get started.
Everything works fine, However, when it comes to Intents related test, the result is strange.
Most of the time, the tests passed in my Mac but fail in my colleague's Windows(not all tests fail) with the the fail message java.lang.IllegalStateException: init() must be called prior to using this method.
Quite strangely, If we Run Debug test in Android Studio flow the code step by step, it passes.
here is the test code:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class MainActivityTest {
#Rule public IntentsTestRule<MainActivity> mRule = new IntentsTestRule<>(MainActivity.class, true, false);
AccountManager accountManager;
MainActivity activity;
private void buildLoginStatus() throws AuthenticatorException {
DanteApp app = (DanteApp) InstrumentationRegistry.getTargetContext().getApplicationContext();
accountManager = app.getDanteAppComponent().accountManager();
DoctorModel doctorModel = AccountMocker.mockDoctorModel();
accountManager.save(doctorModel.doctor);
accountManager.setAccessToken(doctorModel.access_token, false);
}
#Before public void before() throws Exception {
buildLoginStatus();
// must login
assertThat(accountManager.hasAuthenticated(), is(true));
activity = mRule.launchActivity(null);
// block all of the outer intents
intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
}
#After public void tearDown() throws Exception {
accountManager.delete();
}
// failed
#Test public void testViewDisplay() throws Exception {
// check tabhost is displayed
onView(withClassName(equalTo(TabHost.class.getName()))).check(matches(isDisplayed()));
// check toolbar is displayed
onView(withClassName(equalTo(ToolBar.class.getName()))).check(matches(isDisplayed()));
}
// passed
#Test public void testCallServiceHotline() throws Exception {
// switch to the account tab layout
onView(withChild(withText(R.string.account))).perform(click());
// click account menu to make a service call
onView(withId(R.id.contact)).perform(click());
// check call start expectly
intended(allOf(
not(isInternal()),
hasAction(Intent.ACTION_DIAL),
hasData(Uri.parse("tel:" + activity.getString(R.string.call_service)))
));
}
// failed
#Test public void testOpenSettingsUI() throws Exception {
// stub all internal intents
Intents.intending(isInternal())
.respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
onView(withChild(withText(R.string.account))).perform(click());
onView(withId(R.id.setting)).perform(click());
// check open settings activity successfully
intended(anyOf(
hasComponent(SettingActivity.class.getName())
));
}
}
The testing library version(nearly all dependencies are up to date and we use both physics devices and emulator to test):
rule: 0.4.1
runner: 0.4.1
espresso-*: 2.2.1
support-*: 23.1.0
Any idea deserves an appreciation. Thanks!
Two Solutions:
Use ActivityTestRule instead of IntentsTestRule and then in your #Before and #After manually call Intents.init() and Intents.release() respectively.
Write a custom IntentTestRule and override beforeActivityLaunched() to include your AccountManager logic. Use afterActivityFinished for your current #After logic. This will also allow you to just use the default IntentTestRule constructor. (Preferred Solution)
As to why this is happening:
"Finally on an unrelated note, be careful when using the new IntentsTestRule. It does not initialize, Intents.init(), until after the activity is launched (afterActivityLaunched())." - Shameless plug to my own post (halfway down helpful visual)
I think you are running into a race condition where in your #Before method you are executing launchActivity() then espresso tries to execute intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null)); before your activity is actually created, which means afterActivityLaunched() isn't called, which means neither is Intents.init(), crash!
Hope this helps.
IntentsTestRule is derived from ActivityTestRule and should manage Intents.init() and Intents.release() for you.
However, in my case the IntentsTestRule did not work properly. So I switch back to ActivityTestRule and call Intents.init() before and Intents.release() after the test which sent the Intent.
For more information please see this reference.

Categories

Resources