您如何嘲笑平等?

时间:2019-02-05 20:02:56

标签: unit-testing junit kotlin mockito hamcrest

假设我有一个界面

interface IFoo{
    val foo:String
}

并且我想创建与foo字符串匹配相等的类。

简单的例子:

class A(override val foo:String):IFoo{
    val somethingIrrelevant = "bar"

    override fun equals(other: Any?): Boolean {
        return if(other is IFoo) foo == other.foo else false
    }

    override fun hashCode(): Int {
        return Objects.hash(foo)
    }
}

似乎相对简单,但是这个测试用例:

@Test
fun mockingEquality(){
    //given
    val a = A("alpha")
    val b = A("alpha")

    assertThat(a,`is`(b)) //succeeds


    //when
    val c = mock(A::class.java)
    whenever(c.foo).thenReturn("alpha")

    //then
    assertThat(c, `is`(a)) //fails
}

失败

Expected: is <A@589b17d>
     but: was <Mock for A, hashCode: 263885523>

那是为什么? 如何正确模拟A类以使测试成功?

1 个答案:

答案 0 :(得分:1)

这里的问题是,当您创建模拟时,该对象没有任何行为,除非您明确对其进行模拟。这包括您的equalshashCode方法。

在您的示例中,一个“解决方案”将是模拟equalshashCode方法,但是显然这不会给您的测试增加任何价值。琐碎地讲,您可以倒转会起作用的断言(assertThat(a, is(c))),因为最终结果将是a.equals(c),并且a是类A的真实实例,而不是模拟并具有.foo属性模拟。

我怀疑您的示例只是过于简化,但是在这些给定的情况下,您应该更喜欢创建一个真实的实例而不是模拟(例如val c = A("alpha")而不是mock(A::class.java))。

这里的一些其他方法可能是:

另一种方法是,如果您可以获得类的真实实例,则可以使用间谍。例如:

val c = spy(A("other value"))
doReturn("mock value").whenever(c).foo 

但是您可以在documentation中看到,不建议使用这些部分模拟类的方法。