I am new to Mockito and trying to understand how to use doAnswer in order to test a void method.
Here's my class with the onDestroy method to test:
public class TPresenter implements TContract.Presenter {
private CompositeSubscription viewSubscription;
//.......
#Override public void onCreate(.......) {
this.viewSubscription = new CompositeSubscription();
//.......
}
#Override public void onDestroy() {
if(viewSubscription != null && !viewSubscription.isUnsubscribed()) {
viewSubscription.unsubscribe();
}
}
Now I want to write a test for onDestroy() namely to verify that after executing onDestroy the subscription is unsubscribed. I found several examples to use doAnswer for testing void methods, for example here, and also here but I do not understand them.
Please show how to test the method onDestroy.
The normal way how you could test your onDestroy() would be based on viewSubscription being a mocked object. And then you would do something like:
#Test
public testOnDestroyWithoutUnsubscribe() {
when(mockedSubscription.isUnsubscribed()).thenReturn(false);
//... trigger onDestroy()
verifyNoMoreInteractions(mockedSubscription);
}
#Test
public testOnDestroyWithUnsubscribe() {
when(mockedSubscription.isUnsubscribed()).thenReturn(true);
//... trigger onDestroy()
verify
verify(mockedSubscription, times(1)).unsubscribe();
}
In other words: you create a mocked object, and you configure it to take both paths that are possible. Then you verify that the expected actions took place (or not, that is what the first test case does: ensure you do not unsubscribe).
Of course, you can't test the "subscription object is null" case (besides making it null, and ensuring that no NPE gets thrown when triggering the onDestroy()!
Given the comment by the OP: one doesn't necessarily have to use mocking here. But when you want to test a void method, your options are pretty limited. You have to observe side effects somehow!
If you can get a non-mocked viewSubscription instance to do that, fine, then do that. But if not, then somehow inserting a mocked instance is your next best choice. How to do the "dependency injection" depends on the exact context, such as the mocking/testing frameworks you are using.
Testing void methods in your main class under test is not a problem as does not require doAnswer.
Here is an example of how could you go about testing the call to unsubscribe.
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class TPresenterTest {
#InjectMocks
private TPresenter target = new TPresenter();
#Mock
private CompositeSubscription viewSubscription;
#Test
public void onDestroyShouldUnsubscribeWhenSubscriptionNotNullAndUnsubscribed() {
when(viewSubscription.isUnsubscribed()).thenReturn(false);
target.onDestroy();
verify(viewSubscription).unsubscribe();
}
#Test
public void onDestroyShouldNotUnsubscribeWhenSubscriptionNotNullAndNotUnsubscribed() {
when(viewSubscription.isUnsubscribed()).thenReturn(true);
target.onDestroy();
verify(viewSubscription, never()).unsubscribe();
}
}
As I mentioned in my comment to #GhostCat 's answer, my example is in fact un-testable because of the "new" instance of CompositeSubscription class. I would have to re-factor it and #GhostCat 's comment to his/her answer shows a way to do it.
Related
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();
}
#RunWith(MockitoJUnitRunner.Silent.class)
public class LoginActivityTest {
#InjectMocks
LoginActivity loginActivity;
private Pattern emailPattern;
#Before
public void createLogin(){
this.emailPattern = Patterns.EMAIL_ADDRESS;
}
#Test
public void checkValidation(){
mock(LoginActivity.class);
UserVO userVO = new UserVO();
userVO.setEmailID("invalid");
userVO.setPassword("a");
boolean b = loginActivity.validatesFields(userVO);
assertFalse(b);
}
}
this.emailPattern = Patterns.EMAIL_ADDRESS; This is creating null pointer object in MockitoJunitTestClass. But, when I run this on Activity it gets initialized properly.
Use PatternsCompat instead of Patterns
I was having a similar problem because it was just a simple test, but when I added #RunWith(AndroidJUnit4::class) the problem was fixed. Check if this is test that must run with Android resources or not.
I am a little confuse with your test:
You are mocking LoginActivity.class but not setting anything with that. I believe you want to do something like loginActivity = mock(LoginActivity.class); instead.
Also, your are mocking instead spying the class, so it won't access the real method in order to verify the flow of this method. In other words, your test is doing nothing in fact.
Finally, this emailPattern is never used on your test (probably it is used on you code), so I believe you want to mock it (I am supposing it). What I recommend you do is something like this:
#RunWith(MockitoJUnitRunner.Silent.class)
public class LoginActivityTest {
#Spy
#InjectMocks
private LoginActivity loginActivity;
#Mock
private OtherStuff otherStuff;
#Test
public void checkValidation(){
UserVO userVO = new UserVO();
userVO.setEmailID("invalid");
userVO.setPassword("a");
doReturn(Patterns.EMAIL_ADDRESS).when(otherStuff).doStuff();
boolean result = loginActivity.validatesFields(userVO);
assertFalse(result);
}
}
What I did here is just an example of unit test which is validating what validateFields() is doing. I suppose that inside this method you have some method on, what I name otherStuff, which calls a method that returns Patterns.EMAIL_ADDRESS, which is what you want to mock.
It would be really better if you insert the LoginActivity code to be more precise here, but I hope I helped you.
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..
How to verify a void method call in Robolectric test case where as no data coming out the called method.
What to assert in this case? Below given an example of the requirement.
public class SampleClass(){
final String TAG = SampleClass.class.getSimpleName();
public void log(){
Log.d(TAG, "Entry Loggd");
}
}
#Test
public void logEntry_test(){
SampleClass sc = new SampleClass();
sc.log();
// What to assert here to verify this log method
}
First off, good on you for writing tests!!! There are a few ways to go about testing that an internal logger is called. It's equally as important to understand what you're looking to test. Testing that a class is logging a specific message is most likely a fragile test, so be fore-warned that you probably don't need it.
Method #1: Using Robolectric
Robolectic documentation doesn't lend itself to answering basic questions, but its codebase is very well documented with its tests. A basic understanding of its principles and how shadows work can get you a long way. ShadowLog tests lay the ground work to this solution.
#RunWith(RobolectricTestRunner.class)
public class SampleClassTest {
#Test
public void log_writesExpectedMessage() {
new SampleClass().log();
ShadowLog.LogItem lastLog = ShadowLog.getLogs().get(0);
assertThat(lastLog.msg).isEqualTo("some message");
// or
assertThat(lastLog.msg).isNotNull();
}
}
Tests using Robolectric v3.1.2
Add the following to your build.gradle file:
testCompile 'org.robolectric:robolectric:3.1.2'
Method #2: Making use of Abstractions
If your sample class derives from an Android class (Activity, Fragment, Application, etc), then using android.util.Log makes sense, but bear in mind that your test will need to be a Robolectric or AndroidInstrumented test. If your SampleClass is just some POJO, then using a simple logging framework may make your testing efforts easier. For example, using Jake Wharton's Timber, your class and test can be written as follows:
import timber.log.Timber;
public class SampleClass {
void log() {
Timber.d("some message");
}
}
// SampleClassTest.java
public class SampleClassTest {
// setting up a Tree instance that we define below
TestTree testTree = new TestTree();
#Test
public void log_writesExpectedMessage() {
// setting up Timber to us the test classes log writer
Timber.plant(testTree);
// invoke the logging function
new SampleClass().log();
// assert
assertThat(testTree.lastMessage).isEqualTo("some message");
}
private class TestTree extends Timber.Tree {
private String lastMessage;
#Override
protected void log(int priority, String tag, String message, Throwable t) {
lastMessage = message;
}
}
}
Good luck, happy testing!
In my understanding you want to mock static methods. I guess, using static mocks are not the most elegant way to testing. Better to use an abstraction as recommended by abest. Although, it can be done with PowerMock.
I have a Tool class with two static methods, doSomething(Object) and callDoSomething(). The names are intuitive in that callDoSomething delegates its call to doSomething(Object);
public class Tool
{
public static void doSomething( Object o )
{
}
public static void callDoSomething()
{
doSomething( new Object());
}
}
I have a Test class for Tool and I'd like to verify if doSomething(Object) was called (I want to do Argument Matching too in the future)
#RunWith( PowerMockRunner.class )
#PrepareForTest( { Tool.class } )
public class ToolTest
{
#Test
public void toolTest()
{
PowerMockito.mockStatic( Tool.class );
Tool.callDoSomething();// error!!
//Tool.doSomething();// this works! it gets verified!
PowerMockito.verifyStatic();
Tool.doSomething( Mockito.argThat( new MyArgMatcher() ) );
}
class MyArgMatcher extends ArgumentMatcher<Object>
{
#Override
public boolean matches( Object argument )
{
return true;
}
}
}
Verify picks up doSomething(Object) if it's called directly. I've commented this code out above. Verify does NOT pick up doSomething(Object) when using callDoSomething, (this is the code shown above). This is my error log when running the code above:
Wanted but not invoked tool.doSomething(null);
However, there were other interactions with this mock.
at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.performIntercept(MockitoMethodInvocationControl.java:260)
at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.invoke(MockitoMethodInvocationControl.java:192)
at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:105)
at org.powermock.core.MockGateway.methodCall(MockGateway.java:60)
at Tool.doSomething(Tool.java)
at ToolTest.toolTest(ToolTest.java:22)
... [truncated]
I'd like to avoid making any changes to the Tool class. My question is, how can I verify doSomething(Object) was called from callDoSomething(), as well as perform some argument matching on doSomething's param
It sounds like you want to use a static spy (partial mock). The section of the PowerMock documentation that talks about mocking static has a note in the second bullet that could be easily missed:
(use PowerMockito.spy(class) to mock a specific method)
Note, in your example you're not actually mocking the behavior, just verifying the method is called. There's a subtle but important difference. If you don't want doSomething(Object) to be called you'd need to do something like this:
#Test
public void toolTest() {
PowerMockito.spy(Tool.class); //This will call real methods by default.
//This will suppress the method call.
PowerMockito.doNothing().when(Tool.class);
Tool.doSomething(Mockito.argThat( new MyArgMatcher() ));
Tool.callDoSomething();
//The rest isn't needed since you're already mocking the behavior
//but you can still leave it in if you'd like.
PowerMockito.verifyStatic();
Tool.doSomething(Mockito.argThat( new MyArgMatcher() ));
}
If you still want the method to fire though, just remove the two lines for doNothing(). (I added a simple System.out.println("do something " + o); to my version of Tool.java as an additional verification of doNothing().)
You can do your validation with this:
public class Tool{
public static boolean isFromCallDoSomethingMethod= false;
public static void doSomething(Object o){
}
public static void callDoSomething() {
doSomething(new Object());
isFromCallDoSomethingMethod= true;
}
}
You can do the verification as:
if(Tool.isFromCallDoSomethingMethod){
//you called doSomething() from callDoSomething();
}
REMEMBER
Don't forget to do the validation if you call the doSomething() from another way that is not from callDoSomething(), you can do this by ussing Tool.isFromCallDoSomethingMethod = false
Is this what you want?