Mockito - right way to test a method with params - android

I need your assistance please:
I have this method:
public class MyTestClass {
protected void foo(JSONObject result, String text){
String userId = result.optString("name");
mApi.sendInfo(userId, text, mListener);
}
}
In Mockito I do:
#Test
public void whenFooIsCalledThenSendInfoGetsCalled(){
MyTestClass testClassSpy = spy(mMyTestClass);
JSONObject jsonOb = mock(JSONObject.class);
when(jsonOb.optString("name")).thenReturn("something");
testClassSpy.foo(eq(jsonOb), anyString());
....
some verification....
The problem is, the when the foo method gets called, JSONObject result is null.
I can't seem to get this to work. I thought that if I mock the object and make it return a String once optString("name") is called, would solve this issue but it seems NPE is all I get.
What am I doing wrong?
Thanks you

I am not from Java world but when I look on this code snippet I'm not sure what you want to test. If you want to verify exactly what your test method suggest whenFooIsCalledThenSendInfoGetsCalled then:
You should create spy for mApi
You should create stub for JSONObject result
You should use real implementation of MyTestClass
So your SUT class should allow for injecting dependencies:
public class MyTestClass {
private mApi;
public MyTestClass(Api api) {
mApi = api;
}
void foo(JSONObject result, String text){ /* your implementation */}
}
And your test method:
#Test
public void whenFooIsCalledThenSendInfoGetsCalled(){
// arrange test
Api spyApi = spy(Api.class);
JSONObjec stub = mock(JSONObject.class);
when(stub.optString("name")).thenReturn("something");
MyTestClass sut = new MyTestClass(spyApi);
// Act
sut.foo(stub, "text");
// Assert
verify(spyApi , times(1)).foo(eq("something"), "text", listener);
}

Related

Mockito Error -"there were zero interactions with this mock"

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
// Send SiteCatalyst data
//create trigger to adobe analytics when the view visible
mMeasurement = MeasurementWrapper.create(MeasurementEnum.SPLASH);
mMeasurement.trackSplashState();
here I called trackSplashState inside onCreate method in Splash screen.
#Override
public void trackSplashState() {
HashMap<String, Object> data = createCommonData();
MeasureServiceImpl.StartStatus status = mMeasureService.getLaunchStatus();
switch (status) {
case INSTALL: {
data.put("appevent.install", "install");
break;
}
case LAUNCH: {
data.put("appevent.launch", "launch");
break;
}
case UPDATE: {
data.put("appevent.update", "update");
break;
}
}
mAnalyticsService.trackState(mType, data);
}
Method functionality inside MeassurementWrapper.java class
#Override
public void trackState(MeasurementEnum mType, HashMap<String, Object> data, String... additionalData) {
try {
String stateName = MeasurementWrapper.DEVICE_NAME + ":" + String.format(mType.getName(), additionalData);
// Check last Adobe Analytic page value is same to the current page value if so avoiding it send to the Adobe analytics tracking
if (!isPageAndUrlMatching(mType,data)) {
Analytics.trackState(stateName, data);
}
} catch (MissingFormatArgumentException e) {
Log.e(this.getClass().getName(), e.getMessage());
}
}
Implementation of the trackState method inside service(AnalyticsServiceImpl).
<-------- Test Class -------------->
#Config(constants = BuildConfig.class, sdk = TestConfig.SDK)
#RunWith(RobolectricTestRunner.class)
public class AdobeAnalyticsTriggerTest {
private ArgumentCaptor<MeasurementEnum> enumArgumentCaptor;
#Module(includes = TestAppModule.class, injects = AdobeAnalyticsTriggerTest.class, overrides = true)
static class TestModule {}
#Inject
Context context;
#Captor
ArgumentCaptor<HashMap<String, Object>> data;
#Captor
ArgumentCaptor<String[]> varargs;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Services.initialize(new AdobeAnalyticsTriggerTest.TestModule()).inject(this);
enumArgumentCaptor = ArgumentCaptor.forClass(MeasurementEnum.class);
}
#After
public void tearDown() throws Exception {
}
#Test
public void shouldTrackSplashscreen_afterOnCreate(){
SplashActivity splashActivity = Robolectric.buildActivity(SplashActivity.class).create().get();
Measurement measurement = mock(splashActivity.mMeasurement);
verify(((MeasurementWrapper) measurement).mAnalyticsService, times(1)).trackState(enumArgumentCaptor.capture(), data.capture());
}
/**
* Mocks measurement (so stuff is not sent to sitecatalyst)
* #return measurement
*/
private Measurement mock(Measurement measurement) {
MeasurementWrapper wrapper = (MeasurementWrapper) measurement;
wrapper.mAnalyticsService = Mockito.spy(new AnalyticsServiceImpl());
// overwrite sendTrackAction() to prevent logs being sent somewhere
doAnswer(invocationOnMock -> null).when(wrapper.mAnalyticsService).trackAction(any(MeasurementEnum.class), (HashMap) anyMapOf(String.class, Objects.class));
// overwrite sendStateAction() to prevent logs being sent somewhere
doAnswer(invocationOnMock -> null).when(wrapper.mAnalyticsService).trackState(any(MeasurementEnum.class), (HashMap) anyMapOf(String.class, Objects.class));
return wrapper;
}
I need to test(shouldTrackSplashscreen_afterOnCreate) trackState method of the AnalyticsServiceImpl calling when splash screen call onCreate method. But the issue is that I always get an error from Mockito that Actually, there were zero interactions with this mock.Can someone help me to fix this issue. It will be a big help for me. Thanks and regards.
You need to add you're test code between the mocking and the verification part, example see below.
Currently there is no method call done on the mock, hence you get the error message from mockito.
#Test
public void shouldTrackSplashscreen_afterOnCreate(){
SplashActivity splashActivity = Robolectric.buildActivity(SplashActivity.class).create().get();
Measurement measurement = mock(splashActivity.mMeasurement);
// ... add test code here ...
verify(((MeasurementWrapper) measurement).mAnalyticsService, times(1)).trackState(enumArgumentCaptor.capture(), data.capture());
}

PowerMock argument captor returns null for new Intent

Here is my main class and the method that I'm trying to test
public class MyClass {
public void startEmailActivity(FragmentActivity activity, #NotNull String emailUrl) {
if (isMyEmailAppInstalled()) {
Intent myEmailAppIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse(emailUrl));
myEmailAppIntent.setClassName(MY_PACKAGE_NAME, MY_EMAIL_COMPOSE_ACTIVITY_EMAIL);
activity.startActivity(intent);
}
}
And here is the test class and method. Assume that I've mocked necessary calls inside isMyEmailAppInstalled() method such that it returns true
#RunWith(PowerMockRunner.class)
#PrepareForTest(Uri.class)
public class MyClassTest {
#Mock
FragmentActivity mockActivity;
#Mock
private Uri mockUri;
#Captor
private ArgumentCaptor<Intent> intentArgumentCaptor;
private static final String MOCK_EMAIL_URL = "mailto:mock#mock.com";
#Test
public void testStartEmailActivity() throws Exception {
doNothing().when(mockActivity).startActivity(any(Intent.class));
mockStatic(Uri.class);
when(Uri.parse(MOCK_EMAIL_URL)).thenReturn(mockUri);
MyClass testObject = new MyClass();
testObject.startEmailActivity(mockActivity, MOCK_EMAIL_URL);
intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mockActivity).startActivity(intentArgumentCaptor.capture());
Intent sentIntent = intentArgumentCaptor.getValue();
//sentIntent is null here :( Below lines of code throws NPE when test is run
Assert.assertTrue(sentIntent.getComponent().getClassName().equalsIgnoreCase(MY_EMAIL_COMPOSE_ACTIVITY_EMAIL));
Assert.assertTrue(sentIntent.getComponent().getPackageName().equalsIgnoreCase(MY_PACKAGE_NAME));
}
}
Anyone have an idea why the argument captor would return null? It seems like simple thing, may be I'm missing something.
Aha! I found out the missing piece.
Basically I needed two things.
add the preparefortest for the class under test which will allow powermock to get inside this and help with mocking i.e
#PrepareForTest({Uri.class, MyClass.class})
mock the construction of new object using whenNew()
whenNew(Intent.class).withAnyArguments().thenReturn(mockIntent);
So this is what my final test method looks like
#Test
public void testStartEmailActivity() throws Exception {
doNothing().when(mockActivity).startActivity(any(Intent.class));
mockStatic(Uri.class);
when(Uri.parse(MOCK_EMAIL_URL)).thenReturn(mockUri);
when(mockIntent.setClassName(MY_PACKAGE_NAME, MY_EMAIL_COMPOSE_ACTIVITY_EMAIL)).thenReturn(mockIntent);
whenNew(Intent.class).withAnyArguments().thenReturn(mockIntent);
MyClass testObject = new MyClass();
testObject.startEmailActivity(mockActivity, MOCK_EMAIL_URL);
verify(mockActivity).startActivity(mockIntent);
}

ArgumentCaptor captures wrong class

SOLVED: Even if the getValue() on the Argument Captor shows you this, it is normal. To be honest I was expecting to see and instance on the OnLoginWithEmailCallback interface here. The problem on my side was related to a method call on mView which was generating a NPE. Works like a charm now.
ORIGINAL PROBLEM:
I am implementing my first unit test using Mockito in my MVP app and I need to mock the behaviour of a callback when the user is logging in. I am using Firebase to handle the authentication.
I followed a very good tutorial from here : https://fernandocejas.com/2014/04/08/unit-testing-asynchronous-methods-with-mockito/.
I am calling method on class under test. This method calls another one on the Auth Presenter which does the actual work
mPresenter.performLoginWithEmail(EMAIL, PASSWORD);
Then I am verifying that an underlining method in the Auth Presenter class was called. I try to capture the callback interface.
verify(mAuthPresenter, times(1)).login(mOnLoginWithEmailCallbackArgumentCaptor.capture(),
eq(EMAIL), eq(PASSWORD));
The problem is that getValue() from the Argument Captor returns an instance of the mPresenter (class under test) instead of the OnLoginWithEmailCallback interface class. Therefore I get an NPE.
mOnLoginWithEmailCallbackArgumentCaptor.getValue().onLoginWithEmailSuccess();
Here is the complete test class:
#RunWith(MockitoJUnitRunner.class)
public class LoginPresenterTest {
private static String EMAIL = "test#rmail.com";
private static String PASSWORD = "1234";
//class under test
private LoginPresenter mPresenter;
#Mock
AuthPresenter mAuthPresenter;
#Captor
private ArgumentCaptor<OnLoginWithEmailCallback> mOnLoginWithEmailCallbackArgumentCaptor;
#Mock
ILoginActivityView mView;
#Before
public void setupLoginPresenter() {
MockitoAnnotations.initMocks(this);
// Get a reference to the class under test
mPresenter = new LoginPresenter(mView, mAuthPresenter);
}
#Test
public void performLoginWithEmail() {
mPresenter.performLoginWithEmail(EMAIL, PASSWORD);
//wanting to have control over the callback object. therefore call capture to then call methods on the interface
verify(mAuthPresenter, times(1)).login(mOnLoginWithEmailCallbackArgumentCaptor.capture(),
eq(EMAIL), eq(PASSWORD));
mOnLoginWithEmailCallbackArgumentCaptor.getValue().onLoginWithEmailSuccess();
InOrder inOrder = Mockito.inOrder(mView);
inOrder.verify(mView).goToMap();
inOrder.verify(mView).hideProgressBar();
}
}
EDIT: This is the call to mAuthPresenter.login:
SOLVED: getLoginActivityView() was causing an NPE
public void performLoginWithEmail(String email, String password) {
mAuthPresenter.login(new OnLoginWithEmailCallback() {
#Override
public void onLoginWithEmailSuccess() {
getLoginActivityView().goToMap();
getLoginActivityView().hideProgressBar();
}
#Override
public void onLoginWithEmailFailed(String error) {
getLoginActivityView().hideProgressBar();
getLoginActivityView().showToast(error);
}
}, email, password);
}
I also tried using using doAnswer from Mockito:
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
((OnLoginWithEmailCallback)invocation.getArguments()[0]).onLoginWithEmailSuccess();
return null;
}
}).when(mAuthPresenter).login(
any(OnLoginWithEmailCallback.class, EMAIL, PASSWORD));
Still, invocation.getArguments() return an instance of the class under test (LoginPresenter), so the same problem as before. Can you help me?
The problem was that i got confused by the Argument Captor which returned me an instance of the caller class (LoginPresenter). The problem in my case was with a method inside the anonymouse class OnLoginWithEmailCallback() which was throwing an NPE.

how to test picasso using unit-test and mockito

I am learning how to unit-testing in android studio. as shown below, I would like to test the two methods shown below in the code section.
can you please help and guide me how to test this method?
code
public RequestCreator requestCreatorFromUrl(String mPicUrl)
{
return Picasso.with(mCtx).load(mPicUrl);
}
public void setImageOnImageView(RequestCreator requestCreator, ImageView mImagView)
{
requestCreator.into(mImagView);
}
My Attempts:
#Test
public void whenRequestCreatorFromUrlTest() throws Exception {
Picasso mockPicasso = mock(Picasso.class);
File mockFile = mock(File.class);
Assert.assertNotNull("returned Request creator is not null",
mockPicasso.load(mockFile));
}
First method you can't test, you'd have to verify the call of a static method which is not supported in Mockito.
You could split the method in
public RequestCreator requestCreator() {
return Picasso.with(mCtx);
}
and
public void load(RequestCreator requestCreator, String picUrl) {
requestCreator.load(picUrl)
}
and test the load(...) method.
Second method:
Mock the requestCreator. Mock the imageView.
Call the method with your mocked objects.
Then verify requestCreator.into(...) was called with the supplied parameter:
Mockito.verify(requestCreator).into(imageView);

How to mock Callback answer for static methods with PowerMock?

I need to mock some static methods, that's fine so far and can be done like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest({DataService.class})
public class PlayersAllViewModelTest {
// mock objects
private PlayersAllContextHandler mContextHandler;
private PlayersAllAdapter mAdapter;
#Before
public void setUp() throws Exception {
mockStatic(DataService.class);
//define mocks
mContextHandler = mock(PlayersAllContextHandler.class);
mAdapter = mock(PlayersAllAdapter.class);
}
#Test
public void check_init_requests_are_done() throws Exception {
// create instance of viewmodel
new PlayersAllViewModel(mContextHandler, mAdapter);
// check dataservice is requested for method 'getAllPlayers()'
PowerMockito.verifyStatic();
DataService.getAllPlayers(any(DataServiceCallback.class));
}
I need to test the behavior for a given response (success() / failure()) answered in a callback. The normal way to do so is like this:
// define mock answer
doAnswer(new Answer<MyCallback<String>>() {
#Override
public MyCallback answer(InvocationOnMock invocation) throws Throwable {
MyCallback<Player> callback = (MyCallback<Player>) invocation.getArguments()[0];
callback.onFailure(new UnsupportedOperationException());
return null;
}
}).when(>>mocked class instance<<).myTestMethod(any(MyCallback.class));
Because is want to call a static method, i can't do it like that so. There's no mocked instance of a class that could fill the gap :(
Does anybody know what's the correct way to do it?

Categories

Resources