How to mock Callback answer for static methods with PowerMock? - android

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?

Related

Mockito 3.4.0 Static Mocking Exception

I am getting the following exception when trying to mock the static method.
For SettingsUtility, static mocking is already registered in the
current thread To create a new mock, the existing static mock
registration must be deregistered
#Before
fun setUp() {
mockStatic(SettingsUtility::class.java) {
`when` { SettingsUtility.method(app) }.thenReturn { "" }}
}
The returned object's MockedStatic.close() method must be called upon completing the test or the mock will remain active on the current thread.
I am not sure if it is the same as how its done in Java. Hope this Java code snippet helps
private static MockedStatic<SettingsUtility> mockedSettings;
#BeforeAll
public static void init() {
mockedSettings = mockStatic(SettingsUtility.class);
}
#AfterAll
public static void close() {
mockedSettings.close();
}
Try doing this way, you will not get this error. It worked for me.
try(MockedStatic mocked = mockStatic(SettingsUtility.class)) {
mocked.when(SettingsUtility::method).thenReturn("whatever you want");
}
Building on #Sudha 's answer.
in Java you can use #BeforeClass and #AfterClass
private static MockedStatic<SettingsUtility> mockedSettings;
#BeforeClass
public static void init() {
mockedSettings = mockStatic(mockedSettings.class);
}
#AfterClass
public static void close() {
mockedSettings.close();
}
Mockito Inline works differently than the Mockito hence the failure. So what needs to be done is initialise and close the mocks explicitly.
You need to initialise the mockedStatic for your class
private MockedStatic<YourClassWithStaticMethod> mockedStatic;
Then add below code in BeforeEach and AfterEach
#BeforeEach
public void init() {
mockedStatic = mockStatic(YourClassWithStaticMethod.class);
}
#AfterEach
public void cleanup() {
mockedStatic.close();
}
And now you can set the mocked expectations.
mockedStatic.when(YourClassWithStaticMethod::staticMethodToMock).thenReturn(yourReturnedMockedObject);
I tried below and worked for me.
MockedStatic mockedStatic = mockStatic(SettingsUtility.class)
mocked.when(SettingsUtility::method).thenReturn("any thing");
...........
//do all stuf
and at last close the mock
mockedStatic.close();

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 private Observable inside public method?

I am new with tests.
I have something like next code and wish to cover it with unitTests using the Mockito:
public void doSomeJob(){
//some code before
getMvpView().execute(getObservable());
//some code after
}
private Observable<Boolean> getObservable(){
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(#NonNull ObservableEmitter<Boolean> e) throws Exception {
Thread.sleep(5000);
e.onNext(true);
e.onComplete();
}
});
}
so questions:
how correct write test for getMvpView().execute(getObservable()); using Mokito?
how can i verify result of getObservable()?
If your private method is not a part of the interface, i.e. cannot be reached from outside the class, it's not something you should test (presumably it's not, since it's private). Mockito in turn doesn't provide mocking of private methods. Thereby you either need to change your interface (make this data available outside) or leave it without testing.
What you should test is the effect of calling the public methods of your class under test. If you do so you will be able to freely refactor the implementation details later, and your tests will still verify that your class works as expected.
I suppose that your code is part of a presenter implementation and the getMvpView() method returns a view interface:
public class MvpPresenterImpl {
private MvpView view;
public void doSomeJob(){
//some code before
getMvpView().execute(getObservable());
//some code after
}
public void attachView(MvpView view) {
this.view = view;
}
private MvpView getMvpView() {
return view;
}
private Observable<Boolean> getObservable(){
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(#NonNull ObservableEmitter<Boolean> e) throws Exception {
Thread.sleep(5000);
e.onNext(true);
e.onComplete();
}
});
}
}
You can test the effect of doSomeJob() like so:
public class MvpPresenterImplTest {
private MvpPresenterImpl presenter;
private MvpView mockView;
#Before
public void setUp() throws Exception {
// Create a mock view instance so that we can verify method calls on it
mockView = mock(MvpView.class);
// Create our object under test, and set it up with the mock view
presenter = new MvpPresenterImpl();
presenter.attachView(mockView);
}
#Test
public void doSomeJob_callsExecuteOnViewWithCorrectObserver() throws Exception {
// What we want to test is the effect of invoking a public method.
presenter.doSomeJob();
// Verify that the execute method has been called by your class
// under test, and save the parameter for later.
ArgumentCaptor<Observable<Boolean>> paramCaptor =
ArgumentCaptor.<Observable<Boolean>>forClass((Class)Observable.class);
verify(mockView).execute(paramCaptor.capture());
// Get the actual observable that the execute method was called with.
Observable<Boolean> param = paramCaptor.getValue();
// Get a test observer so that we can check what our Observable emits
// (TestObserver is a built-in feature of RxJava, not Mockito.)
TestObserver<Boolean> test = param.test();
// Assert that the Observable behaves as expected
test.assertComplete();
test.assertResult(true);
}
}

How to unit test methods posted by Event Bus in android?

I am using Otto's event bus in my application. In one of my classes I am posting the event.
MyEvent myevent = new MyEvent();
uiBus.post(myEvent);
I am able to test the post method.
Now there is another class which is receiving the event.
//ReceiverClass.java
#Subscribe
public void onEventReceived(MyEvent myevent) {
callAMethod();
}
How do I unit test that this method was invoked. I tried with the following test code
#Mock
Bus uiBus;
#Test
public void testBusReceviedEvent() {
ReceiverClass instance = new ReceiverClass();
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myEvent);
//Test
verify(instance, times(1)).callAMethod();
}
But this code doesn't work.
I'm a little late to the party but here is an example of a class which works and accounts for async calls. Instead of Mocking EventBus we simply let it do it's thing and register it in the TestDriver class below.
The thing that makes this work is the CountDownLatch which, with the help of the abstract DataTransferCallback class, waits for latch.countDown() to be called or 5 seconds to go by.
Just register your test class and in the #Subscribe method, pass it back to the method that created the DataTransferCallback and do your assertions there.
#RunWith(AndroidJUnit4.class)
public class TestDriver {
private final CountDownLatch latch = new CountDownLatch(1);
private EventBus eventBus;
private DataTransferCallback transferCallback;
public abstract class DataTransferCallback {
abstract void onSuccess(DataTransfer event);
}
#Before
public void setUp() {
EventBus.getDefault().register(this);
eventBus = spy(EventBus.getDefault());
}
#SuppressWarnings("unchecked")
#Test
public void test200Resposne() throws InterruptedException {
// Get known good JSON
final String json = TestJSON.get200Response();
// Class under test
final Driver driver = new Driver(InstrumentationRegistry.getTargetContext());
final JsonParser jsonParser = new JsonParser();
//boolean to hold our test result
final boolean[] testPassed = new boolean[]{false};
transferCallback = new DataTransferCallback() {
#Override
public void onSuccess(DataTransfer event) {
assertNotNull(event);
verify(eventBus).post(event);
assertThat(event.getStatus(), is("OK"));
assertTrue(event.getData() != null);
testPassed[0] = true;
}
};
//Set our test EventBus object
driver.setEventBus(eventBus);
// The actual method under test
driver.parseData(jsonParser.parse(json));
// Set a countdown latch to wait for the result (5s)
latch.await(5000, TimeUnit.MILLISECONDS);
// will wait here until 5s or the #Subscrube method is hit
assertTrue(testPassed[0]);
}
//Because we want to examine EventBus Output, register it
//to this class and pass the event back through our custom abstract class
#Subscribe
public void onReceiveEventBusEvent(DataTransfer event) {
assertNotNull(transferCallback);
transferCallback.onSuccess(event);
//notify latch so that we can proceed
latch.countDown();
}
}
It does not work because instance is not a mock. You will have to verify the effects of callAMethod or put that method in another class and inject a mock of this new class into your ReceiverClass class.
For example...
private class ReceiverClass {
private MyNewClass theNewClassIWasTalkingAbout;
// Stick in a setter for that ^
#Subscribe
public void onEventReceived(MyEvent myevent) {
theNewClassIWasTalkingAbout.callAMethod();
}
}
Then your test will have to change slightly...
#Mock
private MyNewClass mockNewClass;
#InjectMocks // This will be the "solid" implementation of the thing you are trying to test, it is not a mock...
private ReceiverClass instance;
#Test
public void testBusReceivedEvent() {
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myevent);
verify(mockNewClass, times(1)).callAMethod();
}
Hope this helps.

Categories

Resources