【问题标题】:detect if private static final field's method is called with mockito检测是否使用 mockito 调用私有静态最终字段的方法
【发布时间】:2016-06-24 13:04:28
【问题描述】:

我想在我的班级中测试onFailure 方法并检测post 方法是否在BUS 实例上被调用。

我已经尝试了一整天,但我无法完成。我将 powermock 和 Mockito 与 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));
    }
}
}

我的测试类 - 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());
 }
}

【问题讨论】:

  • 以下答案都不适合您?

标签: java unit-testing junit mockito powermock


【解决方案1】:

一般来说,我对任何“如何使用 powermock”问题的回答是:根本不使用它。

我花了太多时间在 powermock 测试上突然失败......从来没有发现我的生产代码有任何问题。

相反:“对 powermock 的需求”实际上是一个很好的指标,表明您提出了一个不可测试的设计。而不是使用 powermock 将您的代码“锤击”成似乎可测试的东西......你最好花时间修复你糟糕的设计。

请记住:PowerMock 操作您的字节码,您不是测试您的类,而是 PowerMock 从您的类中创建的东西。

作为初学者,您可能希望至少观看此google tech series. 的前两个视频

【讨论】:

  • 其实我不太同意你的看法。您想要模拟静态方法或验证私有方法是有正当理由的。
  • 嗯,验证私有方法对我来说就是验证实现细节。您验证其他对象用于与您的类通信的公共方法是否执行了它们应该执行的操作。您需要实现这个或那个私有方法的事实应该完全隐藏在您的类中;在进行白盒单元测试时甚至没有出现。当然,如果您绝对必须测试一些您不拥有的代码;并且使用静态,那么 PowerMock 是你最后的手段。但你即将进入那里的痛苦之地。
  • 尽管这不能回答 OP 的问题,但我同意你关于使用 PowerMock 来“锤击”你的代码的观点。
  • @IgorGanapolsky 谢谢 ;-)
【解决方案2】:

这是一个更好的重构案例。这样,您可以在包相邻测试中使用测试替身,并且可以根据需要在新的相邻类中覆盖。

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));
    }
  }
}

如需更完整的替代策略列表,请参阅How to use Mockito when we cannot pass a mock object to an instance of a class

旁注:您的测试类是抽象的。您可能需要考虑在测试中创建一个具体的子类(具有简单的实现)。我看到你使用 Mockito 来提供抽象方法,这是可能的,但它不会提供关于 API 表面 是否改变的有用数据点——这值得测试,作为外部代码(子类) 将依赖该总合同。

【讨论】:

    【解决方案3】:

    您只能验证模拟对象上的方法调用。您必须模拟对 BusProvider.getInstance() 的调用结果,然后您将能够验证对该模拟对象的方法调用。

    @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());
       }
    }
    

    【讨论】:

    • 这个测试通过了吗?
    猜你喜欢
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 2012-09-29
    • 1970-01-01
    • 2013-06-09
    • 2014-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多