I have an Android app that I'm working on and trying to write unit tests for it. The app is written with the MVP architecture and I am trying to test the Presenter-class.
Simplified method I'm trying to test looks like this:
public void userPressedButton() {
service.loadData(new Callback<Data>{
#Override
onResponse(Data data) {
view.showData(data);
}
});
}
Now I want to verify that when the userPressedButton method is called view.showData(data) is called.
I have tried several approaches but I can't seem to figure out how to test this.
Any ideas?
Edit: to clarify, I want to write a unit test
Interesting case.
What i would do is to:
1) - Create a concrete class for that particular Callback:
public class MyCallback implements Callback<Data>{
private View view;
public MyCallback(View view){
this.view = view;
}
#Override
onResponse(Data data) {
view.showData(data);
}
}
Now for this class you can write a unit test which would check whether the onResponse method calls the showData method of the view field.
2) Having extacted the implementation to a concrete class, from the perspective of the class which contains the userPressedButton method, it really is not essential what happens inside of the Callback class.
It is important that a concrete implementation of that interface has been passed:
public void userPressedButton() {
service.loadData(new MyCallback(view));
}
and finally the test:
#InjectMocks
MyClass myClass;
#Mock
Service service;
#Captor
ArgumentCaptor argCaptor;
#Before
public void init(){
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldUseMyCallback(){
// Arrange
// set up myClass for test
// Act
myClass.userPressedButton();
Mockito.verify(service).loadData(argCaptor.capture());
// Assert
assertTrue(argCaptor.getValue instance of MyCallback);
}
So we check whether the loadData method has been called with proper implementation.
Thats how i would test your case.
You could "store" the callback and use a test callback during your test
class YourClass {
private ??? view;
private Callback<Data> callback;
// for testing purposes
protected YouClass(Callback<Data> callback) {
this.callback = callback;
}
public YouClass() {
this(new Callback<Data>{
#Override
onResponse(Data data) {
view.showData(data);
}
});
}
public void userPressedButton() {
service.loadData(this.callback);
}
}
then use some custom callback for your test
Even more simple solution. If this is MVP, you can pass view instance to presenter class. Then test invocation on Mock.
This is what a test method would look like:
MVPView view = mock(MVPView.class);
Presenter presenter = new Presenter(view)
presenter.userPressedButton();
verify(view, atLeastOnce()).showData(any(Data.class));
If the call is asynchronious, then wait for the result, by modifying the last statement:
verify(view, timetout(5000).atLeastOnce()).showData(any(Data.class));
Related
I'm a new one in android dev, so I have an app which contain viewPager with 2 UI fragments and 1 nonUIFragment in which operations are performed (i used "setRetainInstance(true)", it deprecated, but i must use it). In this nonUIFragment i have Handler which accepts messages from operations started with ExecutorServices.
But now my task is test this app with Mockito and i'm totaly confused.
Mentor said "you have to mock the operation that produces the result, is performed in a nonUIFragment, and its result is stored in a collection."
How must look this test, I can't create spy() class NonUIFragment and use real methods because of "Method getMainLooper in android.os.Looper not mocked."
All of my methods are void, they don't returne something, how can i trace this chain.
NonUIFragment.java
private NonUIToActivityInterface nonUIInterface;
private final Map<DefOperandTags, HashMap<DefOperationTags, String>> allResultsMap
= new HashMap<>();
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
//Handler pass result to here
public void passAndSaveResult(DefOperandTags operandTag, DefOperationTags operationTag, String result) {
allResultsMap.get(operandTag)).put(operationTag, result);
}
private final Handler handler = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
if (msg.what != null)
passAndSaveResult(defOperandTags, defOperationTag, msg.obj.toString());
};
OneOfOperation.java (add value to the List)
public class AddToStartList extends Operation {
public AddToStartList(List list, DefOperationTags operationTag) {
super(list);
key = operationTag;
}
#Override
public void operation(Object collection) {
((List)collection).add(0, "123");
}
So, how can I implement what my mentor said?
This is going to be tricky, because your Android testing library has no implementations, and static methods are generally more difficult to mock safely and effectively.
Recent versions of Mockito have added the ability to mock static methods without using another library like PowerMock, so the first choice would be something like that. If at all possible, use mockStatic on Looper::getMainLooper to mock.
Another solution is to add some indirection, giving you a testing seam:
public class NonUIFragment extends Fragment {
/** Visible for testing. */
static Looper overrideLooper;
// ...
private final Handler handler = new Handler(
overrideLooper != null ? overrideLooper : Looper.getMainLooper()) {
/* ... */
};
}
Finally, if you find yourself doing this kind of mock a lot, you can consider a library like Robolectric. Using Robolectric you could simulate the looper with a ShadowLooper, which would let you remote-control it, while using Mockito for any classes your team has written. This would prevent you from having to mock a realistic Looper for every test, for instance.
How do I listen ActivityTestRule's beforeActivityLaunched() method in an android test.
My workaround is creating a custom ActivityTestRule and providing a callback on constructor. Is it a bad practice? Same way is it OK to listen for ActivityTestRule constructor method.
Here is my code:
public class CustomActivityTestRule<A extends Activity> extends ActivityTestRule<A> {
public interface onBeforeListener{
void onBefore(String message);
}
private onBeforeListener listener;
public CustomActivityTestRule(Class<A> activityClass, onBeforeListener listener) {
super(activityClass);
}
#Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
listener.onBefore("before activity launch");
}
}
In android test class, I can do something like:
#Rule public CustomActivityTestRule<MainActivity> mainActivityActivityTestRule = new
CustomActivityTestRule<MainActivity>(MainActivity.class, new CustomActivityTestRule.onBeforeListener() {
#Override
public void onBefore(String message) {
//do something before activity starts
}
});
Same way it is able to do something on junit rule instantiating. Is there any other way to listen for junit test rule instantiating?
You can override beforeActivityLaunched without creation of a new class.
I'm using the following in my tests:
#Rule
public ActivityTestRule<MainActivity> mainActivityActivityTestRule = new ActivityTestRule<MainActivity>(MainActivity.class) {
#Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
}
};
You can configure the test rule so it does not start your activity automatically.
#Rule
public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<MainActivity>(MainActivity.class, false, false);
In you setup method you can prepare all what you need and then launch the activity.
activityTestRule.launchActivity(null);
See also
JavaDoc ActivityTestRule constructor
JavaDoc launchActivity method
I am using mockito to test the call to an Interface, but I get some problems when I want to verify that the interface method 'goToLoginInterface()' was called consecutively when I call to 'goToLogin()'. It is supposed to be something simple but I've been trying to find a solution for hours.
I put and assert to verify that 'getActivityParent()' is effectively returning the mock Interface object, and it is!, so I don't know what the problem is.
public class LoginSimpleFragment extends Fragment {
private ActivityInterface mParentActivity;
public interface ActivityInterface {
void goToLoginInterface();
}
public ActivityInterface getActivityInterface(){
return mParentActivity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.login_simple, container, false);
}
public void goToLogin() {
getActivityInterface().goToLoginInterface();
}
}
This is my test class
#Config(manifest = "../app/src/main/AndroidManifest.xml", emulateSdk = 18)
#RunWith(RobolectricTestRunner.class) // <== REQUIRED for Robolectric!
public class TestLoginActivity {
#Test
public void testPositiveButtonAction() throws Exception {
LoginSimpleFragment mockLoginSampleFragment =
mock(LoginSimpleFragment.class);
LoginSimpleFragment.ActivityInterface mockInterface =
mock(LoginSimpleFragment.ActivityInterface.class);
Mockito.doNothing().when(mockInterface).goToLoginInterface();
//doReturn(mockInterface).when(mockLoginSampleFragment).getActivityInterface();
when(mockLoginSampleFragment.getActivityInterface()).thenReturn(mockInterface);
mockLoginSampleFragment.goToLogin();
assert( Mockito.mockingDetails(mockLoginSampleFragment.getActivityInterface()).isMock() );
verify(mockInterface).goToLoginInterface();
}
}
the output test said:
Wanted but not invoked:
activityInterface.goToLoginInterface();
-> at co.mobico.mainactivities.TestLoginActivity.testPositiveButtonAction(TestLoginActivity.java:35)
Actually, there were zero interactions with this mock.
TestLoginActivity.java:35 is the line 'verify(mockInterface).goToLoginInterface()', at the end of test function
Can you helpme to make the test pass?, I'm using TDD in Android with robolectric, so if I cannot get solve it, I cannot continue working, Thanks!
You are lost in a maze of mocks.
You're not actually using a LoginSampleFragment, you're using a Mock of that class. So when you call goToLogin(), nothing happens, because the mock won't run your normal class code.
Even if you would instruct your mock to do something when you call goToLogin(), at this point you aren't testing your code anymore, you're just testing your own mock setup, spinning in circles.
This might be a good reading: When should I mock?
I know that if I need to mock a static method, this indicates that my design has some issue, but in my case this does not seem to be a design issue.
BundleContext bundleContext = FrameworkUtil.getBundle(ConfigService.class).getBundleContext();
Here FrameworkUtil is a class present in an api jar. Using it in code cant be a design issue.
my problem here is while running this line
FrameworkUtil.getBundle(ConfigService.class);
returns null So my question, is there any way by which I can replace that null at runtime
I am using Mockito framewrok and my project does not allow me to use powermock.
if I use
doReturn(bundle).when(FrameworkUtil.class)
in this way getBundle method is not visible since its a static method.
You are correct that is not a design issue on your part. Without PowerMock, your options become a bit murkier, though.
I would suggest creating a non-static wrapper for the FrameworkUtil class that you can inject and mock.
Update: (David Wallace)
So you add a new class to your application, something like this
public class UtilWrapper {
public Bundle getBundle(Class<?> theClass) {
return FrameworkUtil.getBundle(theClass);
}
}
This class is so simple that you don't need to unit test it. As a general principle, you should only EVER write unit tests for methods that have some kind of logic to them - branching, looping or exception handling. One-liners should NOT be unit tested.
Now, within your application code, add a field of type UtilWrapper, and a setter for it, to every class that currently calls FrameworkUtil.getBundle. Add this line to the construtor of each such class
utilWrapper = new UtilWrapper();
And replace every call to FrameworkUtil.getBundle with utilWrapper.getBundle.
Now in your test, you make a mock UtilWrapper and stub it to return whatever Bundle you like.
when(mockUtilWrapper.getBundle(ConfigService.class)).thenReturn(someBundleYouMade);
and for the class that you're testing, call setUtilWrapper(mockUtilWrapper) or whatever. You don't need this last step if you're using #InjectMocks.
Now your test should all hang together, but using your mocked UtilWrapper instead of the one that relies on FrameworkUtil.
unit test
package x;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class GunTest {
#Before
public void setUp() throws Exception {
}
#Test
public void testFireTrue() {
final Gun unit = Mockito.spy(new Gun());
Mockito.doReturn(5).when(unit).getCount();
assertTrue(unit.fire2());
}
#Test
public void testFireFalse() {
final Gun unit = Mockito.spy(new Gun());
Mockito.doReturn(15).when(unit).getCount();
assertFalse(unit.fire2());
}
}
the unit:
fire calls the static method directly,
fire2 factors out the static call to a protected method:
package x;
public class Gun {
public boolean fire() {
if (StaticClass.getCount() > 10) {
return false;
}
else {
return true;
}
}
public boolean fire2() {
if (getCount() > 10) {
return false;
}
else {
return true;
}
}
protected int getCount() {
return StaticClass.getCount();
}
}
The problem
A presenter that "manages" a passive view subscribes to events that occur in that view (e.g. button click), and does not directly expose the methods that handle those events as public interface. I don't like the idea to make those methods public just for unit-testing since it smells like exposing the internal implementation details. Therefore, calling that event handling code becomes quite non-trivial.
My solution
The view mock has to "intercept" the event subscription and then the corresponding intercepted listener is used to call the event handling code. My implementation includes a utility class that implements the Answer interface from the Mockito API
private class ArgumentRetrievingAnswer<TArg> implements Answer {
private TArg _arg;
#Override
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
_arg = (TArg)args[0];
return null;
}
public TArg getArg() {
return _arg;
}
}
The event subscription is intercepted in the following way
XyzView xyzView = mock(XyzView.class);
ArgumentRetrievingAnswer<OnEventListener> xyzViewTouchedListenerInterceptor =
new ArgumentRetrievingAnswer<OnEventListener>();
doAnswer(xyzViewTouchedListenerInterceptor)
.when(xyzView).addViewTouchedListener(any(OnEventListener.class));
After creating the SUT instance...
XyzPresenter sut = new XyzPresenter(xyzView);
...I obtain the listener
OnEventListener xyzViewTouchListener = xyzViewTouchedListenerInterceptor.getArg();
In the "Act" part I call the event handling method of the listener
xyzViewTouchListener.onEvent();
The question
I'm quite new to unit testing in Java, so I'd like to know if there's any more elegant way of testing the presenter code. The current "Arrange" part is quite bloated an does not seem to excel in readability.
Edit:
Adding the simplified SUT code on Jonathan's request. It illustrates that the presenter does not have any public methods (except constructor), and subscribes to the view events.
public interface XyzView {
void setInfoPanelCaptionText(String text);
void addViewInitializedListener(OnEventListener listener);
void addViewTouchedListener(OnEventListener listener);
}
public class XyzPresenter {
private XyzView _xyzView;
private OnEventListener _xyzViewTouchedListener = new OnEventListener() {
#Override
public void onEvent() {
handleXyzViewTouch();
}
};
public XyzPresenter(XyzView xyzView) {
_xyzView = xyzView;
_xyzView.addViewTouchedListener(_xyzViewTouchedListener);
}
private void handleXyzViewTouch() {
// event handling code
}
}
Basically I also use ArgumentCaptor in this setup.
The basic layout of my presenter tests is like this :
#RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
private Presenter sut;
#Mock
private View view;
#Captor
private ArgumentCaptor<ViewTouchedListener> listenerCaptor;
private ViewTouchedListener listener;
#Before
public void setUp() {
sut = new Presenter(view);
verify(view).addViewTouchedListener(listenerCaptor.capture());
listener = listenerCaptor.getValue();
}
// test methods have access to both sut and its registered listener
}
Thanks to #Jonathan for suggesting ArgumentCaptor, I can use it instead of my "re-invented wheel" ArgumentRetrievingAnswer. I managed to stub void methods for event subscribing to use ArgumentCaptor, although it has some after-taste of a hack.
ArgumentCaptor<OnEventListener> xyzViewTouchedListenerCaptor =
ArgumentCaptor.forClass(OnEventListener.class);
doNothing().when(xyzView).addViewTouchedListener(xyzViewTouchedListenerCaptor.capture());