如何在Kotin中使用Mockito模拟方法调用并返回模拟值?

时间:2018-07-30 13:34:32

标签: unit-testing kotlin mockito

有人可以帮助我弄清楚为什么我嘲笑并命名为“ repo”的kotlin变量仍在执行一种方法,告诉模仿者嘲笑吗?让我告诉你我的意思:

我有一个简单的用例类,如下所示:

class WeatherDetailsPresenter @Inject constructor(var getCurrentWeatherWithForecastUsecase: GetCurrentWithForcastedWeatherUsecase) : BaseMvpPresenter<WeatherDetailsView>() {

fun presentCurrentAndForcastedWeather(country: String?) {
    with{getCurrentWeatherWithForecastUsecase) {
        this.countyName = country
        execute(object : DefaultSubscriber<Pair<CurrentWeatherModel, ForecastedWeatherModel>>() {
            override fun onSubscribe(d: Disposable) {
                super.onSubscribe(d)
                showLoading(true)
            }

            override fun onNext(t: Pair<CurrentWeatherModel, ForecastedWeatherModel>) {
                super.onNext(t)
                view?.showCurrentWeather(t.first)
            }

            override fun onComplete() {
                super.onComplete()
                showLoading(false)
            }
        })
    } 
}

}

它所要做的就是将国家名称传递给它,它会获得该国家的平均天气以及7天的天气预报。没什么大不了的。它将所有内容组合在一起并更新视图(MVP)。它从另一个名为WeatherDataRepository的类中获取数据,只要它返回我们不在乎的数据,该类的实现就不会导入。

让我告诉您getCurrentWeatherWithForecastUsecase类实际上是什么样的:

class GetCurrentWithForcastedWeatherUsecase @Inject constructor(var weatherRepo: WeatherDataRepository) : BaseUseCase() {

var countyName: String? = null

override fun buildUseCaseObservable(): Observable<Pair<CurrentWeatherModel, ForecastedWeatherModel>> {
   return weatherRepo.fetchCurrentWeatherWith7dayForcast(countyName.toString())
}

}

///看到它很容易,它只需要一个weatherRepository并要求它获取结果。我将其发送回给将显示它的呼叫者。

现在您知道此简单方法的作用,让我向您展示我要测试的内容。我只想简单地测试一下是否显示了当前天气。所以最后我真的想测试是否调用了view?.showCurrentWeather(t.first)

这是我的junit测试用例:

class WeatherDetailsPresenterTest {

lateinit var usecase: GetCurrentWithForcastedWeatherUsecase

@Mock
 lateinit var repo: WeatherDataRepository

@Mock
lateinit var view: WeatherDetailsView

lateinit var presenter: WeatherDetailsPresenter



@Before
fun setUp() {
    MockitoAnnotations.initMocks(this)
    usecase = GetCurrentWithForcastedWeatherUsecase(repo)
    presenter = WeatherDetailsPresenter(usecase)

}

@Test
fun testCurrentWeatherScreenAppears() {

    //arrange

//WHY IS THIS MOCK NOT WORKING ??        
Mockito.`when`(repo.fetchCurrentWeatherWith7dayForcast(anyString())).thenReturn(Observable.just(Pair<CurrentWeatherModel, ForecastedWeatherModel>(ArgumentMatchers.any(CurrentWeatherModel::class.java),ArgumentMatchers.any(ForecastedWeatherModel::class.java))))

    //act
    val testSubscriber = TestObserver<Pair<CurrentWeatherModel, ForecastedWeatherModel>>()
    usecase.execute(testSubscriber)


    //assert
    testSubscriber.assertNoErrors()
    testSubscriber.assertSubscribed()
    testSubscriber.assertValue(Pair(ArgumentMatchers.any(), ArgumentMatchers.any()))
    testSubscriber.assertComplete()

    Mockito.verify(view,times(1)).showCurrentWeather(any())
}

}

单元测试将编译并运行。问题是存储库调用实际上是在调用fetchCurrentWeatherWith7dayForcast而不是仅仅返回模拟响应。

这是我运行测试用例时的错误(失败错误):

java.lang.NullPointerException
at me.org.myweatherapp.data.repositories.WeatherDataRepository.fetchCurrentWeatherWith7dayForcast(WeatherDataRepository.kt:17)
at me.org.myweatherapp.presentation.view.mvp.mainScreen.fragments.weatherdetails.WeatherDetailsPresenterTest.testCurrentWeatherScreenAppears(WeatherDetailsPresenterTest.kt:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

您无需查看存储库代码,但无论如何我都会显示它:

class WeatherDataRepository @Inject constructor(val retrofit: Retrofit){

//THIS SHOULD NEVER BE CALLED, SINCE IM MOCKING IT, BUT ITS CALLED, WHY??
fun fetchCurrentWeatherWith7dayForcast(countyName: String): Observable<Pair<CurrentWeatherModel, ForecastedWeatherModel>> {
    val weatherApiService = retrofit.create(WeatherApi::class.java)
    return weatherApiService.getCurrentWeather(countyName)
            .zipWith<ForecastedWeatherModel,Pair<CurrentWeatherModel, ForecastedWeatherModel>>(weatherApiService.getForecastedWeather(countyName)
                    .map{model-> model.forecast?.forecastday?.forEach { it?.dayName= getDay(it?.date.toString())}; model }
                    , BiFunction { t1, t2 -> Pair(t1,t2) })

    }}

我已经注释了要模拟的仓库,然后在设置中初始化了所有模拟,所以我唯一想到的就是我没有立即使用Mockito.when命令?任何帮助表示赞赏。

更新:

这里是故障的整个堆栈轨迹:

"/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java" -ea -Didea.test.cyclic.buffer.size=1048576 -Didea.launcher.port=57805 "-Didea.launcher.bin.path=/Applications/Android Studio.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath "/Applications/Android Studio.app/Contents/lib/idea_rt.jar:/Applications/Android Studio.app/Contents/plugins/junit/lib/junit-rt.jar:/Applications/Android Studio.app/Contents/plugins/junit/lib/junit5-rt.jar:/Users/me/Library/Android/sdk/platforms/android-27/data/res:/Users/me/Documents/mystuff/weatherappdemo/app/build/intermediates/classes/prod/debug:/Users/me/Library/Android/sdk/extras/m2repository/com/android/support/constraint/constraint-layout-solver/1.0.2/constraint-layout-solver-1.0.2.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-v4-27.1.1.aar/8c528b77506492992ca9cefd2fbe3fc1/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.21/88bfff5aa470143a83b0bc5ec00c0be8cabd7cad/kotlin-stdlib-jdk7-1.2.21.jar:/Users/me/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.2.41/e4d6728a4fa55dbfb510aeea25c072c7c51d94/kotlin-reflect-1.2.41.jar:/Users/me/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common-java8/1.0.0/a7458929c2f84a8a59b6b4da631a36f07091cf79/common-java8-1.0.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-media-compat-27.1.1.aar/8c1ab73749282027c9894ed4f6387856/res:/Users/me/.gradle/caches/transforms-1/files-1.1/support-media-compat-27.1.1.aar/8c1ab73749282027c9894ed4f6387856/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.bouncycastle/bcprov-jdk15on/1.50/be504b4901d75cbe129a178f5830e6c358ec214c/bcprov-jdk15on-1.50.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/stetho-1.5.0.aar/f176def4c2ceb952e3ab306a7d56241a/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/rules-1.0.1.aar/88459f9e0846eb9599bad2e0efbab6bb/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-auth-base-license-11.8.0.aar/1f271bdf7747c7247ce3e2d7b7a8ff8e/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-basement-license-11.8.0.aar/9ae67be5ece95255b9bcca7d0378d2b4/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-iid-11.8.0.aar/951a9ce1737846087ee53aaecfb671c0/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-fragment-27.1.1.aar/227f5a73c4224f35e666b27741d51529/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.3.0/bcacde6a8ccedcc56c127403d26b76072fe6214d/retrofit-2.3.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/runtime-1.1.0.aar/506f9955d304b32a4564c4b84422fea4/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/design-27.1.1.aar/4f85b6a28d4b1cf224a5e6cb1d4dbc66/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/design-27.1.1.aar/4f85b6a28d4b1cf224a5e6cb1d4dbc66/res:/Users/me/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/7af827a5af0341c7eceb2a9ffa27a39e/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/multidex-instrumentation-1.0.2.aar/d67f47874da4e87ad02796a65dc73f61/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-config-11.8.0.aar/bc62621a53fdb88540784b9828b13fde/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/appcompat-v7-27.1.1.aar/23e568de9f4f0daff9b5d756af53aa07/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/appcompat-v7-27.1.1.aar/23e568de9f4f0daff9b5d756af53aa07/res:/Users/me/.gradle/caches/modules-2/files-2.1/org.greenrobot/eventbus/3.0.0/ddd99896e9569eaababbe81b35d80e1b91c4ad85/eventbus-3.0.0.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.8.0/5a11f020cce2d11eb71ba916700600e18c4547e7/okhttp-3.8.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-analytics-impl-11.8.0.aar/5b4765afdec874f91c04f5abe56e5275/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/android.arch.persistence.room/common/1.0.0/1cbd3ddc566125293b13af5c0a65bc015476e83c/common-1.0.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/runtime-1.1.0.aar/1fb10be0704f03034b6aa2314ccf3e4d/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/transition-27.1.1.aar/91b8eaec16a28ef6b3781544873a983d/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/transition-27.1.1.aar/91b8eaec16a28ef6b3781544873a983d/res:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-auth-base-11.8.0.aar/882aae6015201ef880d7ddb46766a289/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/butterknife-8.8.1.aar/cf0409e6eb4d8f95c289b322c09c7470/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/fbcore-1.3.0.aar/881eb80147e1a600eb4fdd7ca2ca248e/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-common-11.8.0.aar/7840ba4061070f9ac09a6db65e355818/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-analytics-impl-license-11.8.0.aar/a56e270bd3ae1a384d54e6bd9a48d02d/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/uiautomator-v18-2.1.0.aar/9b94013d0aeaac82611731b234cecc7b/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-compat-27.1.1.aar/8da86b1dff1f0160efbb1b9cd1b03a26/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-compat-27.1.1.aar/8da86b1dff1f0160efbb1b9cd1b03a26/res:/Users/me/.gradle/caches/transforms-1/files-1.1/espresso-contrib-3.0.1.aar/775bf13abb0c730effcf8406c262c9f3/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/viewmodel-1.1.0.aar/1decb5c20af306b59139842edceb0bf7/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.reactivestreams/reactive-streams/1.0.0/14b8c877d98005ba3941c9257cfe09f6ed0e0d74/reactive-streams-1.0.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-core-11.8.0.aar/40ccc15d85cb93820d69a9a52ee41860/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.jakewharton/butterknife-annotations/8.8.1/bc373fb6bc7bca3035041b924f158fd2b946ee8d/butterknife-annotations-8.8.1.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-android-extensions-runtime/1.2.41/c1fd16a8b0be2f6c95dd43e1560d5b86ff742fdc/kotlin-android-extensions-runtime-1.2.41.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/spots-dialog-0.7.aar/d5614913769f6b38243111c590a8482f/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/spots-dialog-0.7.aar/d5614913769f6b38243111c590a8482f/res:/Users/me/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-27.1.1.aar/5eb343d801e7f29a5d223861abe3b6fc/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/stetho-okhttp3-1.5.0.aar/519dbbb66efddad4a4bfae9c6e76718d/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-iid-license-11.8.0.aar/423297131a354e0bfbd7103a38722964/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.github.ihsanbal/LoggingInterceptor/2.0.2/e0230eb518409058f04f1be8e356d9dfd9d7126b/LoggingInterceptor-2.0.2.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-core-utils-27.1.1.aar/10757417e97a4e5d394b007d55ed39df/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-basement-11.8.0.aar/96b45c45c9e7d0359214ab8878e5b137/res:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-basement-11.8.0.aar/96b45c45c9e7d0359214ab8878e5b137/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.41/7e34f009642702250bccd9e5255866f408962a05/kotlin-stdlib-1.2.41.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/2.0.1/516c03b21d50a644d538de0f0369c620989cd8f0/jsr305-2.0.1.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/imagepipeline-base-1.3.0.aar/a7e27f7f6f4d580103459e14d4d91132/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-base-license-11.8.0.aar/0e66bd26413f298ad4b21d093883f138/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/core-ktx-0.1.aar/b15ebaf7972ab87fc408b81f8cd9b7f8/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/adapter-rxjava2/2.3.0/f436637f9500ab5b8bc32afe556373180894b4a5/adapter-rxjava2-2.3.0.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.google.dagger/dagger/2.9/75a739e59d7ede2f7f425f369955bdd1c2ac122b/dagger-2.9.jar:/Users/me/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.6.14/871c3e49dc6183d0d361601c2f1d11abb1a6b48c/byte-buddy-1.6.14.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/imagepipeline-1.3.0.aar/f7d55bb54a7fcb0dc52ca620f3cb3390/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/rxbinding-2.0.0.aar/71b2449f24db0bbe295118e38c3b099c/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.1.0/edf3f7bfb84a7521d0599efa3b0113a0ee90f85/common-1.1.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/drawee-1.3.0.aar/0d2fd7b15bc817f241bd45a7af724235/res:/Users/me/.gradle/caches/transforms-1/files-1.1/drawee-1.3.0.aar/0d2fd7b15bc817f241bd45a7af724235/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.5/612ecb799912ccf77cba9b3ed8c813da086076e9/objenesis-2.5.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.7/751f548c85fa49f330cecbb1875893f971b33c4e/gson-2.7.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jre7/1.2.41/9e7a6f582de73d9cdc6c56ef4e23604a0ee55768/kotlin-stdlib-jre7-1.2.41.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/logging-interceptor/3.8.0/f765f004f3e201bd6ad0904266e605d7fb776d5/logging-interceptor-3.8.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/recyclerview-v7-27.1.1.aar/5c32c514912225f8996a860cdae759cb/res:/Users/me/.gradle/caches/transforms-1/files-1.1/recyclerview-v7-27.1.1.aar/5c32c514912225f8996a860cdae759cb/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/multidex-1.0.2.aar/3d385f486b764139c13b624fee0b32c0/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-core-ui-27.1.1.aar/10a6e827fb24388f3d393ee95b15305c/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/support-core-ui-27.1.1.aar/10a6e827fb24388f3d393ee95b15305c/res:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-tasks-license-11.8.0.aar/f8cc3f0ba924aff3c7d5cc7c9c56f7e9/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/idling-concurrent-3.0.1.aar/bf368765eb5955d1a89d234f6c5669ee/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/rxjava2-1.0.0.aar/406d1a35f2fb9c28ac1c637df15d49db/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.3.0/9e09011e9767bb76b5e27c9b8223476b93b14631/converter-gson-2.3.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-messaging-license-11.8.0.aar/0c9522887d98bb37f93e8694bc9cab09/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-config-license-11.8.0.aar/6602b35b3e55aebb6ac4665e9872b5fc/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/io.reactivex.rxjava2/rxjava/2.1.1/99895d4dc6d79efbb74360f13c556d95533ad8f8/rxjava-2.1.1.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-auth-api-phone-license-11.8.0.aar/f3df85960ad3eeb19f7be4ce902b978a/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/db-framework-1.0.0.aar/796c04fce9221c438948f9c68e14c88c/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/viewstate-2.0.1.aar/bf016c65c3e0c7dcafda5281f41b4e81/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/db-1.0.0.aar/81d8f63860272b7c43a6709748e52ee9/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/io.reactivex.rxjava2/rxjava/2.1.7/8c6d3f76a0b8ed49e9d49a5af9c80c5fc2091677/rxjava-2.1.7.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-messaging-11.8.0.aar/fbcba383734b49591176a7772066d087/res:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-messaging-11.8.0.aar/fbcba383734b49591176a7772066d087/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-common-license-11.8.0.aar/a853cc000b4a9e97a1e8cb8510bbde3a/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-auth-11.8.0.aar/e8a403bb2bc41b0978151644434be336/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/runner-1.0.1.aar/bc61594971ebf029997add3d9e064acb/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/timber-4.5.1.aar/ed168ba0862d519a47bca16bf4dac2df/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/27.1.1/39ded76b5e1ce1c5b2688e1d25cdc20ecee32007/support-annotations-27.1.1.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-27.1.1.aar/e91ef01a13292102a7a45e6e1080e1fb/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-analytics-license-11.8.0.aar/6b229f59f2b7a192f74a9c7d5a33dfe2/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-auth-api-phone-11.8.0.aar/a76aa8978d4a42b84287b4c62555d7a0/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-analytics-11.8.0.aar/9dfc8e29830fdadd2e7d25b1baa19671/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.parse.bolts/bolts-tasks/1.4.0/d85884acf6810a3bbbecb587f239005cbc846dc4/bolts-tasks-1.4.0.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.13.0/a9283170b7305c8d92d25aff02a6ab7e45d06cbe/okio-1.13.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/fresco-1.3.0.aar/db4a2dd4f4c2cab75bf3453e6639ed58/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/rx2-idler-0.9.0.aar/2592289841865b391355f5d4fc368251/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/espresso-intents-3.0.1.aar/eb5beafb4e6532e880eb8af7a315b9f6/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.reactivestreams/reactive-streams/1.0.1/1b1c911686eb40179219466e6a59b634b9d7a748/reactive-streams-1.0.1.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/mvp-2.0.1.aar/f0614328bd1e1100af13886a9dcde777/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/mvp-2.0.1.aar/f0614328bd1e1100af13886a9dcde777/res:/Users/me/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.1.aar/3d7b614004a6eaa78227a35fd3cee256/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/espresso-web-3.0.1.aar/cf8ebc9ea102e614f709045983fb32a7/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/rxandroid-2.0.1.aar/5516ddab631416f22f025eb065849949/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.0.2.aar/3eb445c5139d11809f5142c08a3b669a/res:/Users/me/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.0.2.aar/3eb445c5139d11809f5142c08a3b669a/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-base-11.8.0.aar/6022e015ee3a8def24bc31049a72d6b5/res:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-base-11.8.0.aar/6022e015ee3a8def24bc31049a72d6b5/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/espresso-idling-resource-3.0.1.aar/12c9fd22796fd8b15ccaf8884ae23c20/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.2.21/86407aed2dc197bd5f61d16e759c4ff43678732a/kotlin-stdlib-jdk8-1.2.21.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.rubylichtenstein/rxtest/1.0.7/43a4d1a2b4dcc14c669f4be81680a4e4b7d3dd1e/rxtest-1.0.7.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.picasso/picasso/2.5.2/7446d06ec8d4f7ffcc53f1da37c95f200dcb9387/picasso-2.5.2.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/livedata-core-1.1.0.aar/518c06f36c54349dedb9b4d7d98fdde5/jars/classes.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-tasks-11.8.0.aar/c516e10968cea0d1c7a34bf5a433c591/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/mockwebserver/3.8.0/6bcc6566897ffae93c62e53ff8e8e71af18d7593/mockwebserver-3.8.0.jar:/Users/me/.gradle/caches/modules-2/files-2.1/com.hannesdorfmann.mosby/mvp-common/2.0.1/d75c9c5577143f3cf3fd63f61526a551f910b56f/mvp-common-2.0.1.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-auth-11.8.0.aar/860f44f092aa569d6e12cc2a8b2f1f16/res:/Users/me/.gradle/caches/transforms-1/files-1.1/play-services-auth-11.8.0.aar/860f44f092aa569d6e12cc2a8b2f1f16/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.1.0/8007981f7d7540d89cd18471b8e5dcd2b4f99167/common-1.1.0.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/timberkt-1.3.0.aar/3061f0b036f5136e670a5c16d103a232/jars/classes.jar:/Users/me/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.6.14/ba1e5ba3a84fb2fbf2f4de9138df19665eec4d59/byte-buddy-agent-1.6.14.jar:/Users/me/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/2.8.9/1afb35b2d77d40567756c379e54c18da3574a96e/mockito-core-2.8.9.jar:/Users/me/.gradle/caches/transforms-1/files-1.1/firebase-auth-license-11.8.0.aar/c11a9a832d941e454c91804c30c3b8e5/jars/classes.jar:/Users/me/Documents/mystuff/weatherappdemo/app/build/intermediates/sourceFolderJavaResources/test/prod/debug:/Users/me/Documents/mystuff/weatherappdemo/app/build/tmp/kotlin-classes/prodDebugUnitTest:/Users/me/Documents/mystuff/weatherappdemo/app/build/tmp/kapt3/classes/prodDebugUnitTest:/Users/me/Documents/mystuff/weatherappdemo/app/build/intermediates/sourceFolderJavaResources/prod/debug:/Users/me/Documents/mystuff/weatherappdemo/app/build/tmp/kapt3/classes/prodDebug:/Users/me/Documents/mystuff/weatherappdemo/app/build/tmp/kotlin-classes/prodDebug:/Users/me/Documents/mystuff/weatherappdemo/app/build/generated/mockable-android-27.v3.jar" com.intellij.rt.execution.application.AppMainV2 com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 me.org.weatherapp.presentation.view.mvp.mainScreen.fragments.weatherdetails.WeatherDetailsPresenterTest

java.lang.NullPointerException
    at me.org.weatherapp.data.repositories.WeatherDataRepository.fetchCurrentWeatherWith7dayForcast(WeatherDataRepository.kt:17)
    at me.org.weatherapp.presentation.view.mvp.mainScreen.fragments.weatherdetails.WeatherDetailsPresenterTest.testCurrentWeatherScreenAppears(WeatherDetailsPresenterTest.kt:49)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:78)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:84)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)

1 个答案:

答案 0 :(得分:0)

我终于看到了我的问题。我太复杂了。首先,演示者应订阅可观察对象,因此我的baseUseCase应该看起来像这样:

public abstract class BaseUseCase {

protected abstract Observable buildUseCaseObservable();

@SuppressWarnings("unchecked")
public Observable retrieveUseCaseObservable(){
            return  this.buildUseCaseObservable()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread());
}

}

然后,实际的测试用例只是模拟用例并查看并测试演示者,现在看来一切正常:

    import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.BDDMockito
import org.mockito.BDDMockito.given
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.runners.MockitoJUnitRunner


@RunWith(MockitoJUnitRunner::class)
class WeatherDetailsPresenterTest {

    @Mock
    lateinit var usecase: GetCurrentWithForcastedWeatherUsecase

    @Mock
    lateinit var view: WeatherDetailsView

    lateinit var presenter: WeatherDetailsPresenter

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
        presenter = WeatherDetailsPresenter(usecase)
        presenter.attachView(view)

    }

    @Test
    fun testCurrentWeatherScreenAppears() {

        //arrange
        given(usecase.retrieveUseCaseObservable()).willReturn(Observable.just(Pair(CurrentWeatherModel(), ForecastedWeatherModel())))

        //act
        presenter.presentCurrentAndForcastedWeather("canada")

        //assert
        BDDMockito.verify(view, BDDMockito.times(1)).showCurrentWeather(any(CurrentWeatherModel::class.java)?:CurrentWeatherModel())
    }
}

the elvis operation is to satisfy kotlin

当我们做演示者呼叫记录时,现在我正在演示者内部进行订阅:

fun presentCurrentAndForcastedWeather(country: String?) {
    getCurrentWeatherWithForecastUsecase.takeUnless { country?.isBlank() == true }?.apply {
        this.countyName = country
        retrieveUseCaseObservable().subscribe(object : DefaultSubscriber<Pair<CurrentWeatherModel, ForecastedWeatherModel>>() {
            override fun onSubscribe(d: Disposable) {
                super.onSubscribe(d)
                showLoading(true)
            }

            override fun onNext(t: Pair<CurrentWeatherModel, ForecastedWeatherModel>) {
                super.onNext(t)
                view?.showCurrentWeather(t.first)
                view?.showForcastWeather(t.second)
            }

            override fun onError(e: Throwable) {
                super.onError(e)
                showError { this@WeatherDetailsPresenter.presentCurrentAndForcastedWeather(country) }
            }

            override fun onComplete() {
                super.onComplete()
                showLoading(false)
            }
        })
    } ?: run { view?.showCountryUnavailable() }
}

我还添加了RxAndroidPlugins.setInitMainThreadSchedulerHandler {Schedulers.trampoline()}  告诉rxJava系统在当前线程上运行,因为无论如何都嘲笑了一切。