我需要用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();
}
这是错误的!比较应该与平等进行比较。通过引用这样做不是一回事。
当我进入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()
返回 YOURSELF。再次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的一些魔法。
祝你好运!