1

I want to test onFailure method in my class and detect if post method is called on BUS instance.

I've been trying all day and I couldn't have accomplish. I use powermock and Mockito with junit.

ApiCallback.java:

package com.example.android.webserver;

import com.example.android.event.OnApiRequestErrorEvent;
import com.example.android.event.OnApiResponseErrorEvent;
import com.example.android.model.ApiRequestError;
import com.example.android.utils.BusProvider;
import com.example.android.utils.ErrorUtils;
import org.greenrobot.eventbus.EventBus;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public abstract class ApiCallback<T> implements Callback<T> {
private final static EventBus BUS;

static {
    BUS = BusProvider.getInstance();
}


@Override
public void onFailure(Call<T> call, Throwable t) {
    BusProvider.getInstance().post(new OnApiRequestErrorEvent(new ApiRequestError(t.getMessage())));
}


protected void handleResponse(Response<?> response, Object object) {
    if (response.isSuccessful()) {
        BusProvider.getInstance().post(object);
    } else {
        BUS.post(new OnApiResponseErrorEvent(ErrorUtils.parseError(response), object));
    }
}
}

My test class - ApiCallbackTest.java:

package com.ashojash.android.webserver;

import com.ashojash.android.utils.BusProvider;
import org.greenrobot.eventbus.EventBus;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;

import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.*;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import retrofit2.Call;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doCallRealMethod;
import static org.powermock.api.mockito.PowerMockito.mockStatic;


@RunWith(PowerMockRunner.class)
@PrepareForTest({BusProvider.class})
public class ApiCallbackTest {

@Mock
private ApiCallback apiCallback;
@Mock
private Call call;
@Mock
private Throwable throwable;

@Before
public void setUp() {
}

@Test
public void should_call_busProvider_to_post_event() throws Exception {
//        given
    doCallRealMethod().when(apiCallback).onFailure(any(Call.class),   any(Throwable.class));
    doCallRealMethod().when(BusProvider.getInstance());
//        when
    apiCallback.onFailure(call, throwable);

//        then
    EventBus bus = BusProvider.getInstace();
    verify(bus).post(anyObject());
 }
}
GhostCat
  • 137,827
  • 25
  • 176
  • 248
Mehrdad Shokri
  • 1,974
  • 2
  • 29
  • 45

3 Answers3

2

In general, my answer to any "how to use powermock" question is: to not use it at all.

I have spent too much time on powermock tests failing all of a sudden ... never finding any problem with my production code.

To the contrary: "the need for powermock" is actually a good indicator that you came up with an untestable design. And instead of using to powermock to" hammer" your code into something that seems testable ... you better spend time fixing your bad design.

And keep in mind: PowerMock manipulates your byte code, you are not testing your classes, but something that PowerMock created out of your classes.

As a starter, you might want to watch at least the two first videos from this google tech series.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Actually, I don't agree with you so much. There are legitimate reasons you want to mock static methods or verify private methods. – Mehrdad Shokri Jun 24 '16 at 13:12
  • 1
    Well, verifying private methods translates to verifying implementation details to me. You verify that the public methods that other objects use to communicate with your class do what they are supposed to do. The fact that you need this or that private method to implemented that should be completely hidden inside your class; and not even show up when doing whitebox-unit testing. And of course, if you absolutely **must** test some code that you don't own; and that uses static, then PowerMock is your last resort. But you are about to enter a land of pain there. – GhostCat Jun 24 '16 at 13:13
  • Even though this doesn't answer OP's question, I agree with you on the notions of using PowerMock to "hammer" your code. – IgorGanapolsky Mar 08 '17 at 14:49
  • @IgorGanapolsky Thanks ;-) – GhostCat Mar 08 '17 at 15:14
2

This is a much better case for refactoring. This way, you can use a test double in package-adjacent tests, and can override in a new adjacent class if you need to.

public abstract class ApiCallback<T> implements Callback<T> {
  private final EventBus bus;

  public ApiCallback() {
    this(BusProvider.getInstance());
  }

  /** Package-private for testing. */
  ApiCallback(EventBus bus) {
    this.bus = bus;
  }

  @Override
  public void onFailure(Call<T> call, Throwable t) {
    bus.post(new OnApiRequestErrorEvent(new ApiRequestError(t.getMessage())));
  }

  protected void handleResponse(Response<?> response, Object object) {
    if (response.isSuccessful()) {
      bus.post(object);
    } else {
      bus.post(new OnApiResponseErrorEvent(
          ErrorUtils.parseError(response), object));
    }
  }
}

For a more-complete list of alternative strategies, see How to use Mockito when we cannot pass a mock object to an instance of a class.

Side note: Your class under test is abstract. You may want to consider making a concrete subclass (with a simple implementation) in your test. I see your use of Mockito to provide the abstract methods, which is possible, but which won't provide the useful data point about whether the API surface changed—which is worth testing, as external code (subclasses) will be relying on that general contract.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
2

You can only verify a method call on a mock object. You have to mock the result of the call to BusProvider.getInstance(), and then you will be able to verify the method call on that mocked object.

@RunWith(PowerMockRunner.class)
@PrepareForTest({BusProvider.class})
public class ApiCallbackTest {

   @Mock
   private ApiCallback apiCallback;

   @Mock
   private Call call;

   @Mock
   private Throwable throwable;

   @Test
   public void should_call_busProvider_to_post_event() throws Exception {
      doCallRealMethod().when(apiCallback).onFailure(any(Call.class),   any(Throwable.class));
      PowerMockito.mockStatic(BusProvider.class);
      EventBus eventBusMock = mock(EventBus.class);
      when(BusProvider.getInstance()).thenReturn(eventBusMock);
      apiCallback.onFailure(call, throwable);

      EventBus bus = BusProvider.getInstace();
      verify(bus).post(anyObject());
   }
}
Rodrigo Villalba Zayas
  • 5,326
  • 2
  • 23
  • 36