【问题标题】:Android. Mockito uses real objects instead of mock安卓。 Mockito 使用真实对象而不是模拟
【发布时间】:2017-03-13 15:43:38
【问题描述】:

所以,我只是在编写简单的测试。 问题是测试运行器运行真正的“存储库”类代码而不是模拟代码...... 你知道为什么我会得到一个例外吗?

测试类

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

【问题讨论】:

    标签: android unit-testing testing mockito kotlin


    【解决方案1】:

    好的,这个不错。这是我发现的:

    测试代码不是问题,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 的内联版本。

    【讨论】:

    • 非常感谢您的帮助!我花了一整天的时间来解决这个问题:)
    • 我已经尝试使用 mockMaker 扩展来进行 mockito,实际上也是这样做的,但它没有用......
    • 扩展解决方案适用于某些项目/模块,但不适用于其他项目/模块。不知道为什么它那么片状。到目前为止,这似乎是可靠的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多