一个带有mockito测试的简单kotlin类导致了MissingMethodInvocationException

时间:2016-09-07 09:10:00

标签: android unit-testing kotlin mockito

我开始学习Kotlin和Mockito,所以我编写了一个简单的模块来测试它。

AccountData_K.kt:

open class AccountData_K {
var isLogin: Boolean = false
var userName: String? = null

    fun changeLogin() : Boolean {
        return !isLogin
    }
}

AccountDataMockTest_K.kt:

class AccountDataMockTest_K {
    @Mock
    val accountData = AccountData_K()

    @Before
    fun setupAccountData() {
        MockitoAnnotations.initMocks(this)
    }

    @Test
    fun testNotNull() {
        assertNotNull(accountData)
    }

    @Test
    fun testIsLogin() {
        val result = accountData.changeLogin()
        assertEquals(result, true)
    }

    @Test
    fun testChangeLogin() {        
        `when`(accountData.changeLogin()).thenReturn(false)
        val result = accountData.changeLogin()
        assertEquals(result, false)
    }
}

当我运行测试时,它会报告有关testChangeLogin()方法的异常:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
   It is a limitation of the mock engine.

at com.seal.materialdesignwithkotlin.AccountDataMockTest_K.testChangeLogin(AccountDataMockTest_K.kt:57)
...

我怀疑为什么这个方法不是模拟方法调用...

所以请帮助我,谢谢。

3 个答案:

答案 0 :(得分:9)

默认情况下Kotlin的classes and members are final。 Mockito无法mock final classes nor methods。要使用Mockito,您需要open您想要模拟的方法:

open fun changeLogin() : Boolean {
    return !isLogin
}

进一步阅读

PS。在我看来,只要你通过ie ISP保持接口很小,使用Mockito或其他模拟框架的测试代码比手写的假货/存根更难以理解和理解。

答案 1 :(得分:4)

正如@miensol所提到的那样,出现问题是因为Kotlin默认情况下类为final。错误消息不是很清楚,尽管这部分提到final是可能的原因之一:

  
      
  1. 你存根:final / private / equals()/ hashCode()方法。
  2.   

在与Mockito的单元测试中,有一个项目专门用于帮助处理Kotlin“默认最终”。对于JUNIT,您可以使用kotlin-testrunner这是一种简单的方法,可以让任何Kotlin测试自动打开类,以便在类加载器加载时进行测试。用法很简单,只需添加一个@RunWith(KotlinTestRunner::class)注释,例如:

@RunWith(KotlinTestRunner::class)
class MyKotlinTestclass {
   @Test 
   fun test() {
   ...
   }
}

Never say final: mocking Kotlin classes in unit tests

一文中详细介绍了这一点

这允许以自动方式覆盖您的用例,允许所有类被模拟,否则将不被允许。

答案 2 :(得分:0)

对于任何发表这篇文章的人,您可以使用kotlin-allopen库打开您的类进行测试:

首先在您的build.gradle(项目)文件中添加依赖项:

dependencies {
        classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
    }

之后,将该插件应用到您的build.gradle(应用程序Mobdule)文件中:

apply plugin: 'kotlin-allopen'

然后指定将打开类的注释列表:

allOpen {
    annotation('com.example.myproject.OpenForTesting')
}

并在项目的名为OpenForTesting的文件中定义注释(文件必须位于项目com.example.myproject.OpenForTesting中):

 /**
     * This annotation allows us to open some classes for mocking purposes while they are final in
     * release builds.
     */
    @Target(AnnotationTarget.ANNOTATION_CLASS)
    annotation class OpenClass

    /**
     * Annotate a class with [OpenForTesting] if you want it to be extendable in debug builds.
     */
    @OpenClass
    @Target(AnnotationTarget.CLASS)
    annotation class OpenForTesting

并对要打开的每个类使用此批注

@OpenForTesting

以下是有关全开放的Kotlin官方文档:https://kotlinlang.org/docs/reference/compiler-plugins.html