我是第一次面临单元测试,我想知道以下情况的最佳方法是什么。 我正在使用Mockito进行测试。以下测试适用于逻辑(演示器)层,我正在尝试验证视图的某些行为。
应用类
需要包含在测试中的演示者方法:
public void loadWeather() {
CityDetailsModel selectedCity = getDbHelper().getSelectedCityModel();
if (selectedCity != null) {
getCompositeDisposableHelper().execute(
getApiHelper().weatherApiRequest(selectedCity.getLatitude(), selectedCity.getLongitude()),
new WeatherObserver(getMvpView()));
} else {
getMvpView().showEmptyView();
}
}
天气观察者:
public class WeatherObserver extends BaseViewSubscriber<DayMvpView, WeatherResponseModel> {
public WeatherObserver(DayMvpView view) {
super(view);
}
@Override public void onNext(WeatherResponseModel weatherResponseModel) {
super.onNext(weatherResponseModel);
if (weatherResponseModel.getData().isEmpty()) {
getMvpView().showEmptyView();
} else {
getMvpView().showWeather(weatherResponseModel.getData());
}
}
}
BaseViewSubscriber(默认DisposableObserver 基类,每当我们需要默认错误处理时都会使用):
public class BaseViewSubscriber<V extends BaseMvpView, T> extends DisposableObserver<T> {
private ErrorHandlerHelper errorHandlerHelper;
private V view;
public BaseViewSubscriber(V view) {
this.view = view;
errorHandlerHelper = WeatherApplication.getApplicationComponent().errorHelper();
}
public V getView() {
return view;
}
public boolean shouldShowError() {
return true;
}
protected boolean shouldShowLoading() {
return true;
}
@Override public void onStart() {
if (!AppUtils.isNetworkAvailable(WeatherApplication.getApplicationComponent().context())) {
onError(new InternetConnectionException());
return;
}
if (shouldShowLoading()) {
view.showLoading();
}
super.onStart();
}
@Override public void onError(Throwable e) {
if (view == null) {
return;
}
if (shouldShowLoading()) {
view.hideLoading();
}
if (shouldShowError()) {
view.onError(errorHandlerHelper.getProperErrorMessage(e));
}
}
@Override public void onComplete() {
if (view == null) {
return;
}
if (shouldShowLoading()) {
view.hideLoading();
}
}
@Override public void onNext(T t) {
if (view == null) {
return;
}
}
}
复合一次性助手(复合一次性助手类):
public class CompositeDisposableHelper {
public CompositeDisposable disposables;
public TestScheduler testScheduler;
@Inject public CompositeDisposableHelper(CompositeDisposable disposables) {
this.disposables = disposables;
testScheduler = new TestScheduler();
}
public <T> void execute(Observable<T> observable, DisposableObserver<T> observer) {
addDisposable(observable.subscribeOn(testScheduler)
.observeOn(testScheduler)
.subscribeWith(observer));
}
public void dispose() {
if (!disposables.isDisposed()) {
disposables.dispose();
}
}
public TestScheduler getTestScheduler() {
return testScheduler;
}
public void addDisposable(Disposable disposable) {
disposables.add(disposable);
}
}
我的测试:
@Test public void loadSuccessfully() {
WeatherResponseModel responseModel = new WeatherResponseModel();
List<WeatherModel> list = new ArrayList<>();
list.add(new WeatherModel());
responseModel.setData(list);
CityDetailsModel cityDetailsModel = new CityDetailsModel();
cityDetailsModel.setLongitude("");
cityDetailsModel.setLatitude("");
when(dbHelper.getSelectedCityModel()).thenReturn(cityDetailsModel);
when(apiHelper.weatherApiRequest(anyString(), anyString())).thenReturn(
Observable.just(responseModel));
dayPresenter.loadWeather();
compositeDisposableHelper.getTestScheduler().triggerActions();
verify(dayMvpView).showWeather(list);
verify(dayMvpView, never()).showEmptyView();
verify(dayMvpView, never()).onError(anyString());
}
当我尝试运行测试时,我得到 NullPointer,因为调用了new WeatherObserver(getMvpView())
,并且在BaseViewSubscriber
errorHandlerHelper 为空,因为 getApplicationCopomnent 为空。 同样,出于同样的原因,NullPointer 在静态方法中被抛出AppUtils.isNetworkAvailable()
。
当我尝试注释这些行时,测试是可以的。
我的问题是:
- 我应该也使用dagger进行单元测试还是?如果是,请给 我的测试示例。
- 我应该将PowerMockito用于静态方法吗
AppUtils.isNetworkAvailable()
?如果是,是否可以仅仅因为 这种方法使用PowerMockito Runner@RunWith(PowerMockRunner.class)
?
我应该也使用dagger进行单元测试还是?如果是,请给我测试的例子。
你不必在测试中使用Dagger,但这就是依赖注入将使您受益的地方,因为它将帮助您去除依赖项,并且测试将能够替换它们。
我应该将 PowerMockito 用于静态方法 AppUtils.isNetworkAvailable()吗?如果是,是否可以仅仅因为这种方法使用 PowerMockito Runner @RunWith(PowerMockRunner.class)?
静态方法通常不利于测试,因为您无法出于测试目的
替换它们(至少不容易且没有 PowerMock)。更好的做法是使用生产代码的Dagger
来注入这些依赖项,最好是在 Constructor 中,因此在测试中,您可以根据测试需求简单地提供这些依赖项(必要时使用模拟或假
)。在您的情况下,您可以将ErrorHandlerHelper
和AppUtils
添加到构造函数BaseViewSubscriber
。 由于不应该注入BaseViewSubscriber
,因此您需要从外部(在演示器中)向它提供这些模块,您应该在其中使用注入来获取这些对象。 再次在构造函数中。
在测试中,只需替换或将此对象提供给演示者,演示者又会将其移交给BaseViewSubscriber
。
您可以在此处阅读有关 android 测试接缝的更多信息。
除此之外,对我来说,包装 Observable 以获得常见行为的Observer
和Disposable
的 OO 层次结构有些奇怪,它本质上打破了面向函数流的反应式方法,您可能需要考虑使用诸如使用Transformers
编写和使用 doOnXXX 运算符等模式确实在反应式流中应用了常见行为。