如何使用 Mockito 对依赖于 equals() 的抽象类方法进行单元测试?



我需要用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()方法将自身与调用抽象方法asCanonicalasCanonical()构建进行比较。

为了测试这一点,我需要为我的抽象类创建一个空的实现,并为两个实现的方法进行 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的一些魔法。

祝你好运!

相关内容

  • 没有找到相关文章

最新更新