Mockito 没有从具有多个 "when" 条件的模拟方法返回正确的结果
Mockito not returning correct result from mocked method with multiple "when" conditions
我有一个简单的 DTO class:
@Data // lombok
public class MyObj {
private int id;
private String someProperty;
}
和一个 class 以及 DTO 对象的一些计算逻辑:
public class MyClass {
public String doSomething(MyObj obj) {
// some calculations
}
}
我试图模拟MyClass
在一些单元测试中使用它,但遇到了一个奇怪的问题。因此,我创建了这个最小的测试代码来说明这个问题:
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
private final MyObj myObjFirst = new MyObj();
private final MyObj myObjSecond = new MyObj();
@Mock
private MyClass myClass;
@Before
public void setUp() {
doReturn("OTHER")
.when(myClass)
.doSomething(any(MyObj.class));
doReturn("FIRST")
.when(myClass)
.doSomething(myObjFirst);
doReturn("SECOND")
.when(myClass)
.doSomething(myObjSecond);
}
@Test
public void doSomething() {
assertEquals("FIRST", myClass.doSomething(myObjFirst)); // fail, actual value "SECOND"
assertEquals("SECOND", myClass.doSomething(myObjSecond));
assertEquals("OTHER", myClass.doSomething(new MyObj()));
assertEquals("OTHER", myClass.doSomething(new MyObj()));
}
}
出于某种原因,myClass.doSomething(myObjFirst)
生成字符串 "SECOND"
而不是预期的 "FIRST"
,因此测试在第一个断言处失败。
我做错了什么?
解决方案
存根时,将预期的对象包装到 Mockito.same
中以通过引用匹配它们( 用于不同的匹配器):
@Before
public void setUp() {
doReturn("OTHER")
.when(myClass)
.doSomething(any(MyObj.class));
doReturn("FIRST")
.when(myClass)
.doSomething(same(myObjFirst)); // <-- HERE
doReturn("SECOND")
.when(myClass)
.doSomething(same(myObjSecond)); // <-- AND HERE
}
说明
Mockito 似乎使用 equals
方法来检测模拟方法的给定参数(需要参考文档)。
由于Lombok's @Data
annotation, a MyObj.equals
implementation is generated, which compares the objects by their fields id
and someProperty
. Because both fields are null
on myObjFirst
and myObjSecond
, and because last stubbing has the highest importance,Lombok 首先将给定myClass.doSomething
的参数与myObjSecond
进行比较,立即找到匹配并返回"SECOND"
。
如果从 MyObj
中省略 Lombok 的 @Data
注释,上面的代码将按原样运行,因为 MyObj.equals
方法将默认为对象引用比较,使得 myObjFirst
不等于 myObjSecond
.
然而,在这种情况下,myObjFirst
等于 myObjSecond
等于 new MyObj()
,除非它们的属性设置为不同的值。
我有一个简单的 DTO class:
@Data // lombok
public class MyObj {
private int id;
private String someProperty;
}
和一个 class 以及 DTO 对象的一些计算逻辑:
public class MyClass {
public String doSomething(MyObj obj) {
// some calculations
}
}
我试图模拟MyClass
在一些单元测试中使用它,但遇到了一个奇怪的问题。因此,我创建了这个最小的测试代码来说明这个问题:
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
private final MyObj myObjFirst = new MyObj();
private final MyObj myObjSecond = new MyObj();
@Mock
private MyClass myClass;
@Before
public void setUp() {
doReturn("OTHER")
.when(myClass)
.doSomething(any(MyObj.class));
doReturn("FIRST")
.when(myClass)
.doSomething(myObjFirst);
doReturn("SECOND")
.when(myClass)
.doSomething(myObjSecond);
}
@Test
public void doSomething() {
assertEquals("FIRST", myClass.doSomething(myObjFirst)); // fail, actual value "SECOND"
assertEquals("SECOND", myClass.doSomething(myObjSecond));
assertEquals("OTHER", myClass.doSomething(new MyObj()));
assertEquals("OTHER", myClass.doSomething(new MyObj()));
}
}
出于某种原因,myClass.doSomething(myObjFirst)
生成字符串 "SECOND"
而不是预期的 "FIRST"
,因此测试在第一个断言处失败。
我做错了什么?
解决方案
存根时,将预期的对象包装到 Mockito.same
中以通过引用匹配它们(
@Before
public void setUp() {
doReturn("OTHER")
.when(myClass)
.doSomething(any(MyObj.class));
doReturn("FIRST")
.when(myClass)
.doSomething(same(myObjFirst)); // <-- HERE
doReturn("SECOND")
.when(myClass)
.doSomething(same(myObjSecond)); // <-- AND HERE
}
说明
Mockito 似乎使用 equals
方法来检测模拟方法的给定参数(需要参考文档)。
由于Lombok's @Data
annotation, a MyObj.equals
implementation is generated, which compares the objects by their fields id
and someProperty
. Because both fields are null
on myObjFirst
and myObjSecond
, and because last stubbing has the highest importance,Lombok 首先将给定myClass.doSomething
的参数与myObjSecond
进行比较,立即找到匹配并返回"SECOND"
。
如果从 MyObj
中省略 Lombok 的 @Data
注释,上面的代码将按原样运行,因为 MyObj.equals
方法将默认为对象引用比较,使得 myObjFirst
不等于 myObjSecond
.
然而,在这种情况下,myObjFirst
等于 myObjSecond
等于 new MyObj()
,除非它们的属性设置为不同的值。