[英]How to unit test an abstract class method that depends on equals() with Mockito?
我需要在Java中测试一个抽象类,并且我有一个依赖于equals()
来提供结果的方法。
public abstract class BaseClass<T extends BaseClass<T>> {
public boolean isCanonical() {
return toCanonical() == this;
}
public T toCanonical() {
T asCanonical = asCanonical();
if (equals(asCanonical)) {
//noinspection unchecked
return (T) this;
}
return asCanonical;
}
protected abstract T asCanonical();
}
正如您所看到的, toCanonical()
方法将自身与asCanonical
构建进行比较,调用抽象方法asCanonical()
。
为了测试这个,我需要创建一个抽象类的空实现,并使mockito为两个实现的方法调用实际方法,并为asCanonical()返回我自己的类。
BaseClass aCanonicalClass;
BaseClass aNonCanonicalClass;
@Mock
BaseClass sut;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
// these are the methods under test, use the real ones
Mockito.when(sut.isCanonical())
.thenCallRealMethod();
Mockito.when(sut.toCanonical())
.thenCallRealMethod();
}
通常,如果这不是equals()
方法,我会这样做:
@Test
public void test_isCanonical_bad() {
// test true
Mockito.when(sut.equals(aCanonicalClass))
.thenReturn(true);
Mockito.when(sut.equals(aNonCanonicalClass))
.thenReturn(false);
Mockito.when(sut.asCanonical())
.thenReturn(aCanonicalClass);
Assert.assertTrue(sut.isCanonical());
// test false
Mockito.when(sut.equals(aNonCanonicalClass))
.thenReturn(true);
Mockito.when(sut.equals(aCanonicalClass))
.thenReturn(false);
Mockito.when(sut.asCanonical())
.thenReturn(aCanonicalClass);
Assert.assertFalse(sut.isCanonical());
}
哪个给出了这个错误:
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. Mocking methods declared on non-public parent classes is not supported. 2. inside when() you don't call method on mock but on some other object.
Mockito的一个限制是它不允许模拟equals()
和hashcode()
方法。
作为一种解决办法时,我想测试的条件我只是通过自己equals() = true
当我想要测试的条件和其他随机对象equals() = false
。
@Test
public void test_isCanonical() {
// test true
Mockito.when(sut.asCanonical())
.thenReturn(sut);
Assert.assertTrue(sut.isCanonical());
// test false
Mockito.when(sut.asCanonical())
.thenReturn(aCanonicalClass);
Assert.assertFalse(sut.isCanonical());
}
但即使我将实现更改为此,此测试也会通过:
public T toCanonical() {
T asCanonical = asCanonical();
if (this == asCanonical) {
//noinspection unchecked
return (T) this;
}
return asCanonical;
}
甚至这个:
public T toCanonical() {
return asCanonical();
}
这是错的! 应该用equals进行比较。 通过引用这样做是不一样的。
当我到达toCanonical()
方法时,事情变得无法测试:
@Test
public void test_toCanonical_bad() {
// test canonical
Mockito.when(sut.equals(aCanonicalClass))
.thenReturn(true);
Mockito.when(sut.equals(aNonCanonicalClass))
.thenReturn(false);
Mockito.when(sut.asCanonical())
.thenReturn(aCanonicalClass);
Assert.assertSame(sut, sut.toCanonical());
// test non-canonical
Mockito.when(sut.equals(aNonCanonicalClass))
.thenReturn(true);
Mockito.when(sut.equals(aCanonicalClass))
.thenReturn(false);
Mockito.when(sut.asCanonical())
.thenReturn(aCanonicalClass);
Assert.assertNotEquals(sut, sut.toCanonical());
Assert.assertSame(aCanonicalClass, sut.toCanonical());
}
这当然不起作用,并且应用相同的解决方法并没有多大意义,因为我只测试函数的返回值是asCanonical()
的返回值:
@Test
public void test_toCanonical() {
// test canonical
Mockito.when(sut.asCanonical())
.thenReturn(sut);
Assert.assertSame(sut, sut.toCanonical());
// test non-canonical
Mockito.when(sut.asCanonical())
.thenReturn(aCanonicalClass);
Assert.assertNotEquals(sut, sut.toCanonical());
Assert.assertSame(aCanonicalClass, sut.toCanonical());
}
这两个测试都没有意义,因为我无法模拟equals()
的结果。
有没有办法测试至少equals()
是用我给它的对象调用的? (间谍)
有没有其他方法来测试这个?
编辑 :这是我正在使用的实际代码的简化。
我修改了示例代码以至少显示
toCanonical()
和asCanonical()
方法应该返回同一个类的实例(而不仅仅是任何类)我想在这里测试基类。 只有具体的实现知道如何构建一个实际的规范类(
asCanonical()
)或检查这个类是否等于规范类(equals()
)。 请注意,toCanonical()
返回toCanonical()
如果它等于自身的规范版本。 再equals != same
。实际的实现是不可变的类。 因为它们很多,而且这个代码对于我把它放在基类中的每个人都是一样的。
你为什么要测试一个抽象类? 如果你真的想测试toCanonical
方法(这不是final
,所以你可以稍后覆盖它),你可以实例化一个临时具体类并测试它。 这比使用Mockito和其他东西更容易。
即:
@Test
public void myAwesomeTest() {
// Arrange
BaseClass test = new BaseClass<String>() {
// provide concrete implementation
protected String asCanonical() {
return "Foo";
}
};
// Act
String result = test.toCanonical("Bar");
// Assert
}
一旦简单的情况工作,您可以将baseclass
的创建提取到工厂方法,即MakeBaseClass(String valueToReturnForAsCanonical
并编写更多测试。
如果您在某些未经测试的代码区域中使用Spy是很方便的,并且您无法在代码中存根零件或创建一些接缝。 在这种情况下,我认为你可以自己编写样板,而不是依靠Mockito的魔法。
祝好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.