Mockito - MissingMethodInvocationException - android

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);
}
}

Related

Why is Mockito boolean stub being ignored

I have a method that checks a condition and does some work.
public void doSomeWork(){
if(!UtilityClass.someCondition()){
context.getmeSomething();
}
}
My test looks like this.
#Test
public void test(){
myClass.doSomeWork();
PowerMockito.verifyStatic(UtilityClass.class)
when(UtilityClass.someCondition()).thenReturn(false);
verify(mContext, times(1)).getmeSomething();
}
The problem is the stub is simply ignored. The test passes regardless of the stub result. By the same token, never verification on fails and I get Never wanted at the test but wanted at my class under test from Mockito. My question is why the boolean stub being ignored?
Update
I am not sure if it is significant with my original question, but the Utility class is included in the prepare for test and has mockStatic call in setUp.
Ordering of your statements!
Register your mocks and behaviour of it.
Call the method
Verify
EDIT:
you should also make sure, that UtilityClass is a mock. You can't stub actual classes, just mocks of them.
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Mock
private UtilityClass utilityClassMock;
private MyClass myClass;
#Before
public void beforeEachTest() {
myClass = new MyClass(utilityClassMock);
}
#Test
public void test(){
when(utilityClassMock.someCondition).thenReturn(false);
myClass.doSomeWork();
verify(mContext, times(1)).getmeSomething();
}
Just like GabrielJoerg said the stubbing should happen first before the method call. But the real issue here is that when stubbing, you should avoid calling verifyStatic. Here is what worked for me.
#Test
public void test(){
when(UtilityClass.someCondition()).thenReturn(false);
myClass.doSomeWork();
verify(mContext, times(1)).getmeSomething();
}

Robolectric 3.8 with MVP and mocking setupActivity()

I use Android MVP architecture and I want to test my View. I have the following code in my project:
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
#Test
public void testDisplay() throws Exception {
MainActivity activity = Robolectric.setupActivity(MainActivity.class);
final Menu menu = Shadows.shadowOf(activity).getOptionsMenu();
assertEquals("Log", menu.findItem(R.id.menu_login).getTitle().toString());
}
}
However, I get ExceptionInIntializationError for some 3rd party library throws it in the Activity's onResume() step which is invoked by Robolectric.
Moreover, in Activity's onResume() the Presenter's method is called which makes a network request.
My questions are:
How to mock 3rd party intialization step? I read about Shadows, but I don't know how to use them in this particular scenario. I should mock stuff during setupActivity(...) call.
How to mock the network call? In my MVP code, the presenter is created in onCreate() method. Should I switch to direct dependency injection pattern and rebuild the architecture? But this approach would conflict with Robolectric.setupActivity(MainActivity.class); as I need some kind of hook to inject stuff in onCreate() or onResume() during Robolectric calls.
Ok, I came up with the following solution. It is strange that I have never seen similar examples on their page or in articles. I have used Mockito along with Robolectric.
Firstly, I have added setPresenter method to Activity(MVP's View) to insert custom-mock Presenter into View
#VisibleForTesting
public void setPresenter(P presenter) {
this.presenter = presenter;
}
In my code, Presenter is created during onCreate() method, so I have added null-check in order to inject Presenter before onCreate():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null)
presenter = providePresenter(App.appComponentFrom(this));
presenter.create();
}
In Robolectric framework I have found buildActivity() method which just invokes Activity's constructor (no lifecycle methods!).
Then after this call I injected my mock-Presenter.
The last step is to call activityController.setup().get() as is does the same job as setupActivity(), which is calling lifecycle methods: create(), resume()
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
#Mock
CheckoutContract.Presenter presenter;
private ShadowActivity mainActivity;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ActivityController<MainActivity> activityController = Robolectric.buildActivity(MainActivity.class);
activityController.get().setPresenter(presenter);
mainActivity = Shadows.shadowOf(activityController.setup().get());
}
#Test
public void checkText() throws Exception {
assertEquals(RuntimeEnvironment.application.getString(R.string.activityMain_title),
((Toolbar)checkoutActivity.findViewById(R.id.vToolbar)).getTitle());
}
}
Voila! In my test I got Context Resources from RuntimeEnvironment and it have passed!

Mockito is verifying the wrong method

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.

How to mock and verify a callback in method using Mockito

Within this method, I want to mock and ensure that mSharedPrefsManager gets called when I don't pass in a certain email string.
#Override
public void retrieveWithEmail(final String email, final WelcomeContract.Presenter presenter)
{
retrieveInteractor.buildRetrieveRequest(email, new RetrieveImpl.OnRetrieveCompletedListener()
{
#Override
public void onRetrieveCompleted(final MaitreBaseGson retrieveResponse, RetrieveImpl retrieveClass)
{
if (retrieveResponse.getStatus().equals(mContext.getString(R.string.ok)))
{
if (!email.equals("certain#email.com"))
mSharedPrefsManager.storePoints(Integer.parseInt(retrieveResponse.getData().getPoints()));
presenter.updateSilhouette(retrieveResponse);
}
// Silently swallow failures
}
});
}
However, with my test I'm not able to catch whether mSharedPrefsManager is called. Mockito says that .storePoints() is never called. I thought about doing a doReturn().when() but as this is within the method that wouldn't work, would it?
How do I catch the interactions on sharedPrefsManager?
Mockito also says that .updateSilhouette() is not called. Do I need to mock onRetrieveCompleted() somehow?
#RunWith(MockitoJUnitRunner.class)
public class WelcomeInteractorTest
{
#Mock
RetrieveImpl retrieveInteractor;
#Mock
WelcomePresenter welcomePresenter;
#Mock
SharedPrefsManager sharedPrefsManager;
#Mock
Context context;
#InjectMocks WelcomeInteractorImpl welcomeInteractor;
#Mock
RetrieveImpl.OnRetrieveCompletedListener onRetrieveCompletedListener;
#Test
public void RetrieveWithCertainEmail_SavePoints()
{
welcomeInteractor.retrieveWithEmail("certain#email.com", welcomePresenter);
verify(retrieveInteractor).buildRetrieveRequest(eq("certain#email.com"), any(RetrieveImpl.OnRetrieveCompletedListener.class));
verify(sharedPrefsManager).storePoints(any(Integer.class));
verify(welcomePresenter).updateSilhouette(any(MaitreBaseGson.class));
}
}
Attempting to use #Spy caused a lot of issues for me as RetrieveImpl interacts with a network.
I instead used a Captor and captured the callback.
#Captor
private ArgumentCaptor<RetrieveImpl.OnRetrieveCompletedListener> mOnRetrieveCompletedListenerCaptor;
...
#Test
public void isTest()
{
...
verify(retrieveInteractor).buildRetrieveRequest(eq(email), mOnRetrieveCompletedListenerCaptor.capture());
mOnRetrieveCompletedListenerCaptor.getValue().onRetrieveCompleted(mockMaitreBaseGsonSuccessful, retrieveInteractor);
}
You are mocking:
#Mock
RetrieveImpl retrieveInteractor;
This means that when you call retrieveInteractor.buildRetrieveRequest(..), the real implementation is not invoked and eventually the methods that you expect to be called within that method call are never called..
Try using #Spy instead, this will actually allow for the real implementation to be called and you can verify that object also:
#Spy
RetrieveImpl retrieveInteractor;
Just one the side.. in think you are testing too much there and going to deep in your verifications.
That test in my opinion should be done for the RetrieveImpl.OnRetrieveCompletedListener class. Not the one that is in your question.
But thats just to my taste..

Mocking in InstrumentationTestCase

I try to mock a method in my instrumentation test but it fails and I am looking for a solution to solve it.
public class MyTest extends InstrumentationTestCase {
private Context mAppCtx;
#Override
public void setUp() throws Exception {
super.setUp();
Context context = mock(Context.class);
mAppCtx = getInstrumentation().getContext().getApplicationContext();
when(mAppCtx.createPackageContext(PACKAGE_NAME, 0)).thenReturn(context);
}
A crash happens on the following line:
when(mAppCtx.createPackageContext(PACKAGE_NAME, 0)).thenReturn(context);
And I got following error:
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.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
It is a limitation of the mock engine.
You need to mock each method invocation of: getInstrumentation().getContext().getApplicationContext();
Example:
Instrumentation inst = mock(Instrumentation.class);
Context instContext = mock(Context.class);
ApplicationContext mAppCtx= mock(ApplicationContext.class);
when(getInstrumentation()).thenReturn(inst);
when(inst.getContext()).thenReturn(instContext);
when(instContext.getApplicationContext()).thenReturn(mAppCtx);
when(mAppCtx.createPackageContext(PACKAGE_NAME, 0)).thenReturn(context);

Categories

Resources