I have a simple test using an activityTestRule that should check whether a method of the activity under test was called:
#Test
public void callLiveLocation() {
MapSettingsActivity spy = spy(activityRule.getActivity());
doNothing().when(spy).setLiveLocation();
onView(withId(R.id.btn_map_current_location)).perform(click());
verify(spy).setLiveLocation();
}
The method setLiveLocation() is being called when I check in debug mode.
However, the console tells me:
Wanted but not invoked: mapSettingsActivity.setLiveLocation();
-> at com.android.dx.mockito.InvocationHandlerAdapter.invoke(InvocationHandlerAdapter.java:53)
Actually, there were zero interactions with this mock.
How do I check if the method of the activity under test was called?
I use Android's databinding for the button click, which invokes a callback, which in turn calls a method on the activity under test.
Note:
The method is a simple method on the activity:
public void setLiveLocation() {
super.startLocationListener();
}
Edit:
I noticed that creating the spy returns null for a yet unknown reason:
MapSettingsActivity spy = spy(activityRule.getActivity());
First of all this is not exact solution of your problem, but maybe it will help you.
So this is how I handled similar problem. In general: in order Mockito could detect method call on spy object, this method must be called on spied object (if you understand what I'm talking about, cause I don`t)). It is not true in your case. Your setLiveLocation called on real instance of activity, which is stored in ActivityTestRule.
My example:
I need to verify that RecyclerView`s adpter updateDataSet() method is called exactly once. This is how I did it in Kotlin:
val adapter = activityTestRule.activity
.find<RecyclerView>(R.id.reviewsList)
.adapter as FlexibleAdapter<ReviewItem>
assertNotNull(adapter)
val spy = spy(adapter)
activityTestRule.activity
.runOnUiThread {
activityTestRule.activity
.find<RecyclerView>(R.id.reviewsList)
.adapter = spy
}
doNothing().`when`(spy).updateDataSet(ArgumentMatchers.any())
onView(withId(R.id.swipeRefresh)).perform(ViewActions.swipeDown())
verify(spy, times(1)).updateDataSet(MOCKED_REVIEWS_FROM_SERVER.map(::ReviewItem))
So basically I get adapter from Recycler and change it to my spy adapter. My test finished successfully only when I made this replacement.
In your case ActivityTestRule holds instance of real activity, so you need somehow to replace it with spy object. I`m not sure how to do it, but I suppose there is chance to create subclass of this rule and to create spy of activity maybe in constructor. Then you will get spied object and use it to verify any calls.
Related
I have a ViewModel in which there is a method which has the following line of code:
billDate.set(!TextUtils.isEmpty(SampleApp.getInstance().getAccountManager().getDueDate()) ?
String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due),
SampleApp.getInstance().getAccountManager().getBillingDueDate()) :
SampleApp.getInstance().getApplicationContext().getString(R.string.missing_due_date));
I have a test class using Mockito to test the different methods in ViewModel. But it is failing with NullPointerException at this line:
String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due),
Below is the log:
java.lang.NullPointerException
at java.util.regex.Matcher.getTextLength(Matcher.java:1283)
at java.util.regex.Matcher.reset(Matcher.java:309)
at java.util.regex.Matcher.<init>(Matcher.java:229)
at java.util.regex.Pattern.matcher(Pattern.java:1093)
at java.util.Formatter.parse(Formatter.java:2547)
at java.util.Formatter.format(Formatter.java:2501)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2940)
While running a test case, I see the log showing some error related to Pattern
Can somebody suggest, how to test the String.format() method?
First of all, you should not be importing android view packages into your ViewModel. So skip using things like TextUtils inside ViewModels.
As to the getApplicationContext().getString(), create an interface for this. Something like:
interface StringProvider {
String getString(int resource);
}
Then pass that interface in your ViewModel constructor and use that to get the string you want.
When you initialize the ViewModel, you can pass a concrete implementation of StringProvider like this:
class StringProviderImpl implements StringProvider {
String getString(int resource) {
return SampleApp.getInstance().getApplicationContext().getString(resource);
}
}
This way, for your unit tests, you can just mock StringProvider and don't have to worry about dealing with contexts inside your ViewModel and the related test code.
You don't need to test the String.format method. That is not your code, and your goal should be to test your own code. But your code is using that method, so you need to test your code. This is the part you are trying to validate or mock out as I understand it:
String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due), SampleApp.getInstance().getAccountManager().getBillingDueDate())
which makes several calls to SampleApp to get an instance. Since those calls to SampleApp.getInstance are static method calls, you won't be able to mock them out. There isn't enough code posted to know what SampleApp is or what SampleApp.getInstance() returns or to know if any of the subsequent calls on that instance are returning null, but one of them is. So I think to solve this you need to look at the what the getInstance method returns. If you can't touch that code and you're hoping to only modify your test classes, you may not be able to test this with mockito due to the static method.
But otherwise you will need to build a way for your tests so the call to SampleApp.getInstance returns a mock object as the instance instead of whatever real instance I presume it is returning now. Then you can mock out the subsequent methods like getApplicationContext and getString to make them return canned responses so that the string.format call will not fail on a null input.
One note of caution--if you do end up making the static getInstance method return a mock, but sure you have proper cleanup when your test is done to set it back to what it was returning originally so you don't inadvertently modify something that might cause another unrelated unit test to fail. That is always a risk if you change something returned by a static method in a unit test since you are effectively changing it for all tests.
Considering that the test fails after the AccountManager was already used, you should have set up the SampleApp as a mock or fake already.
SampleApp app = SampleApp.getInstance()
AccountManager am = app.getAccountManager();
Context context = app.getApplicationContext();
billDate.set(!TextUtils.isEmpty(am.getDueDate()) ?
String.format(context.getString(R.string.due), am.getBillingDueDate()) :
context.getString(R.string.missing_due_date);
Now you only need to make sure to mock the Context you provide with with app.getApplicationContext() or the SampleApp itself, if you use app.getString() directly.
doReturn(dueFormatString).when(context).getString(R.string.due);
doReturn(dueMissingString).when(context).getString(R.string.missing_due_date);
But in general you should abstract the Context away. Not using it will simplify your code and therefore your testing a lot.
Also consider using context.getString() instead of String.format() for formatting a string you load from a resource. It's as easy as adding the format arguments as parameters to the call.
context.getString(R.string.due, am.getBillingDueDate())
I am testing [Using UI Automator] a particular task, where a thread is started when the application class is created. [onCreate()].
Now, I have mocked the entire thread which makes a http call to series of tasks that happen inside the app. [mockedThread]
Now, I use a variable to access this object when TEST=true and run in the normal mode where a http call is made when TEST=false. I make TEST=true only inside the test methods.
Right now the code works in this way -
onCreate() {
if TEST == true {
use mock object and update it manually.
-- mockedObject
} else if TEST == false {
use real object and update it using http calls.
-- real Object
}
}
Now I'm trying to use the mocked object without this if -- else loop of testing = TRUE or FALSE. Is there any was we can do this with mockito or any other way... like dagger2 or something ?
[PS: Please don't advise on Powermock as these are not static object]
I am trying to mock it like this but doesn't work :
testMock() {
try {
AndroidApplication application = Mockito.mock(AndroidApplication.class);
context.startActivity(intent);
mDevice.wait(Until.hasObject(By.pkg(TARGET_PACKAGE).depth(0)), Constants.LAUNCH_TIMEOUT);
Mockito.when(application.getThreadObject()).thenReturn(mockObject);
} catch(MockException m){
m.printStackTracke();
}
}
Error I get it - Actually, there were zero interactions with this mock.
I am trying to test a LoginPresenter, and I am using Mockito for the mocks.
Mockito doesnt let me verify a method invocation on the SUT so I have to mock its dependencies and verify their methods have been called by the LoginPresenter.
I got to the following situation:
LoginPresenter has a method called attemptLogin:
private void attemptLogin(String username, String password) {
new LoginNetworkOperation(username, password).execute();
}
I have to mock LoginNetworkOperation and using Mockito, verify the method invocation on execute().
#Test
public void testWhenUserNameAndPasswordAreEnteredShouldAttemptLogin() throws Exception {
LoginView loginView = Mockito.mock(LoginView.class);
LoginNetworkOperation loginNetworkOperation = Mockito.mock(LoginNetworkOperation.class);
Mockito.when(loginView.getUserName()).thenReturn("George");
Mockito.when(loginView.getPassword()).thenReturn("aaaaaa");
loginPresenter.setLoginView(loginView);
loginPresenter.onLoginClicked();
Mockito.verify(loginNetworkOperation.execute());
}
However, how do I make LoginPresenter use the mocked LoginNetworkOperation instead of the one it creates in the attemptLogin method? I need to change the design of LoginPresenter to use a member variable, and provide a setter for it, which is suboptimal because a local variable in the method is plenty, as it is used only there.
Am I going about this whole thing wrong?
Initially, I wanted to verify that LoginPresenter's attemptLogin is called, but Mockito can only verify mocked objects' methods and I cannot spy on LoginPresenter because it is final (generated by AndroidAnnotations)
I found the answer in this video:
https://www.youtube.com/watch?v=wEhu57pih5w
The biggest takeaway is: don't mix object creation logic with business logic
which means don't instantiate objects in methods or constructors (probably use DI instead), because the test code has no power over what chain-reaction starts whenever a SUT's constructor or a method is called, that is bound to a new keyword.
That means that in my case, the LoginPresenter shouldn't be responsible for creating the LoginNetworkOperation object but should take it from the outside
That way I will be able to tell it to use the mock instead of the concrete implementation, and thus, I will be able to do my tests
I want to test some method, for example:
public class testObj {
..
public void upload(Context context, Data data, Info info, Listener listener, Bla bla, ....) {
...
}
}
now in some cases i just want to know that this method was called, but i do not care about anyy of the arguments passed.
Now calling Mockito.any(Foo.class) is very discouraging, I know i can also use matchers but it's not that great also.
Is there some cleaner way to achive this?
No; verify needs to identify the method you're referring to, which means you'll need to call the correct method signature. Keeping an actual method call will also allow IDEs and automated refactoring tools to search for and modify the calls appropriately.
If you're running your tests from a Java 8 source environment, you can use any() with no argument; Java 8 has improved the ability to infer generic types when given as a parameter.
Though it usually makes more sense just to use matchers and explicit calls, you do have a few similar capabilities:
For stubbing, you can sometimes use a default answer to avoid specifying a lot of redundant calls and method values, but that won't help you with verification.
For verification, you can use MockingDetails.getInvocations() to inspect calls without using the built-in Mockito capabilities.
PowerMockito has private method verification by name, but not the same for public methods.
I have been having quite a bit of trouble implementing unit testing on the Android. As a simple test, I've been trying to match a string retrieved from string resources:
String myString = myActivity.getResources().getString(R.string.testString));
However, when unit testing this invariably results in a null pointer exception. This includes robolectric as well as the Junit implementation delivered with the Android sdk.
One possible solution is to approach the retrieval of resources in a manner similar to a data access object. That is, create an interface through which string resources would be accessed. This would allow me to mock access the string resource. Similarly, I could separate the non-android dependent behavior of, say, an Activity, into a separate pojo class. This would allow me to run unit tests using standard Java testing tools. In fact, I could potentially delegate any Android infrastructure related activity to an interface.
This seems like a lot of jumping through hoops to get to unit testing. Is it worth it? Is there a more viable approach?
It turned out, the problem was that the activity has to be gotten in the actual test method. So, for example, my method now looks like this:
public void testGetActivityResourceString() {
Activity myActivity = this.getActivity();
String myString = myActivity.getResources().getString(R.string.hello);
Assert.assertNotNull(myString);
}
Whereas before I was creating activity in setup. This giveaway was in the docs:
"For each test method invocation, the Activity will not actually be created until the first time this method is called."
This was a real hassle to figure out. The example for HelloWorldTest doesn't work for the same reason.
Here's the full entry:
Public T getActivity ()
Since: API Level 3
Get the Activity under test, starting it if necessary.
For each test method invocation, the Activity will not actually be created until the first time this method is called.
If you wish to provide custom setup values to your Activity, you may call setActivityIntent(Intent) and/or setActivityInitialTouchMode(boolean) before your first call to getActivity(). Calling them after your Activity has started will have no effect.
NOTE: Activities under test may not be started from within the UI thread. If your test method is annotated with UiThreadTest, then your Activity will be started automatically just before your test method is run. You still call this method in order to get the Activity under test.
This works correctly:
public void testGetResourceString() {
assertNotNull(mActivity.getResources()
.getString(com.example.pkg.R.string.testString));
}
Because you haven't provided any of your code but only the getReousrces() line, I will guess what you are doing wrong:
you are not using the correct base class for your test, use ActivityInstrumentationTestCase2 because you need the system infrastructure
you are using the resources of your test project instead of your project under test, that's why in my example the id is com.example.pkg.R.string.testString.