[英]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.