Android系统。 Mockito使用真实对象而不是模拟

时间:2017-03-13 15:43:38

标签: android unit-testing testing mockito kotlin

所以,我只是写简单的测试。 问题是测试运行器运行真正的存储库'类代码而不是模拟代码...... 你知道为什么我会对此有例外吗?

测试课

class SingInFacebookPresenterTest {

    @Mock
    private lateinit var view: SignInFacebookContract.View

    @Mock
    private lateinit var repository: Repository

    @Captor
    private lateinit var callback: ArgumentCaptor<RepositoryCallback.FacebookLoginImp>

    private lateinit var presenter: SingInFacebookPresenter

    private val serverToken = "token"

    @Before
    fun init() {

        MockitoAnnotations.initMocks(this)

        presenter = SingInFacebookPresenter(MockContext(), repository, view)
    }

    @Test
    fun facebook_login_success() {

        //Given
        val token = "token"
        val serverToken = "server token"

        presenter.loginViaFacebook(token)

        //When
        verify(repository).loginViaFacebook(token, callback.capture())


        callback.value.onSuccess(serverToken)

        //Then
        verify(view).success(serverToken)
    }

演示

class SingInFacebookPresenter(var context: Context, var repository: Repository, var view: SignInFacebookContract.View): SignInFacebookContract.Presenter {

    public override fun loginViaFacebook(token: String) {
        repository.loginViaFacebook(token, object : RepositoryCallback.FacebookLoginImp {
            override fun onSuccess(token: String) {
                view.success(token)
            }

            override fun onFailure() {
                view.onFailure()
            }

        })
    }

存储库

open class Repository(context: Context) {

    init {
        ApiHelper(context)
    }

    private var facebookLoginPresenterCallback: RepositoryCallback.FacebookLoginImp? = null

    fun loginViaFacebook(token: String, facebookLoginPresenterCallback: RepositoryCallback.FacebookLoginImp?) {
//        this.facebookLoginPresenterCallback = facebookLoginPresenterCallback
        val signInResponse = ApiHelper.signInViaFacebook(token)
//        signInResponse.enqueue(signInFacebookCallback)
        signInResponse.enqueue(object : Callback<SignInResponse> {
            override fun onResponse(call: Call<SignInResponse>?, response: Response<SignInResponse>?) {
                if (response!!.isSuccessful) {
                    val token = response.body()?.token ?: return
                    facebookLoginPresenterCallback?.onSuccess(token)
                    return
                }
                facebookLoginPresenterCallback?.onFailure()
            }

            override fun onFailure(call: Call<SignInResponse>?, t: Throwable?) {
                facebookLoginPresenterCallback?.onFailure()
            }
        })
    }

    private val signInFacebookCallback = object : Callback<SignInResponse> {
        override fun onResponse(call: Call<SignInResponse>?, response: Response<SignInResponse>?) {
            if (response!!.isSuccessful) {
                val token = response.body()?.token ?: return
                facebookLoginPresenterCallback?.onSuccess(token)
                return
            }
            facebookLoginPresenterCallback?.onFailure()
        }

        override fun onFailure(call: Call<SignInResponse>?, t: Throwable?) {
            facebookLoginPresenterCallback?.onFailure()
        }
    }
}

例外:

Exception in thread "OkHttp Dispatcher" java.lang.NoSuchMethodError: okhttp3.internal.Platform.log(Ljava/lang/String;)V
    at okhttp3.logging.HttpLoggingInterceptor$Logger$1.log(HttpLoggingInterceptor.java:109)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:157)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:190)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:163)
    at okhttp3.RealCall.access$100(RealCall.java:30)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:127)
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Exception in thread "OkHttp Dispatcher" java.lang.NoSuchMethodError: okhttp3.internal.Platform.log(Ljava/lang/String;)V
    at okhttp3.logging.HttpLoggingInterceptor$Logger$1.log(HttpLoggingInterceptor.java:109)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:157)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:190)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:163)
    at okhttp3.RealCall.access$100(RealCall.java:30)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:127)
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

org.mockito.exceptions.base.MockitoException: 
No argument value was captured!
You might have forgotten to use argument.capture() in verify()...
...or you used capture() in stubbing but stubbed method was not called.
Be aware that it is recommended to use capture() only with verify()

Examples of correct argument capturing:
    ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
    verify(mock).doSomething(argument.capture());
    assertEquals("John", argument.getValue().getName());

1 个答案:

答案 0 :(得分:2)

好的,这是一个很好的。这是我发现的:

测试代码不是问题,Mockito出于某种原因创建了Repository的真实(?)实例。或者至少无法拦截某处的方法调用?

无论如何,这是一个完全剥离的示例,用org.mockito:mockito-core:2.7.11重现问题:

open class Greeter {
    fun hello() = "hello world"
}

class MockitoJavaExample {

    @Test
    fun test() {
        val greeter: Greeter = mock()
        println(greeter.hello()) // prints "hello world" which it shouldn't
    }

}

我对Mockito的内部不太熟悉,告诉你这是怎么回事,但转而使用mockito-inline,一种不同的模拟方法解决了这个问题。它还使您能够在Kotlin中模拟非open类,无论如何您都可能想要这样,以便您不必打开扩展类来测试它们。使用此方法,无论该类是否已打开,您都可以正确地获取该函数返回的null,因此它不会使用实际的实现。

您可以使用compile 'org.mockito:mockito-inline:2.7.11'代替-core版本切换到Mockito的内联版本。