[英]Mockito Spy - stub before calling the constructor
我正在試圖窺探一個Object,我想在構造函數調用之前存根一個由構造函數調用的方法。
我的班級看起來像這樣:
public class MyClass {
public MyClass() {
setup();
}
public void setup() {
}
}
不得調用安裝方法。 那么,我如何監視這個方法(和存根設置,以便它什么都不做)?
它可以很好地模擬方法,但我想單元測試MyClass
,所以我需要非常其他的方法。
之所以需要存根設置方法以便它什么都不做:
我正在編寫一個Lego機器人(lejos),我在機器人需要工作的設置中放了一些代碼。 但是,當我在TinyVM(安裝在機器人上的VM)之外調用它時,java崩潰,因為VM尚未正確初始化(因為測試在我的PC上運行)。 對於單元測試,設置並不重要。
我不能存根類/方法設置調用,因為它們中的一些是公共靜態最終變量。
要直接回答您的問題, 您不能使用Mockito來存根從構造函數調用的方法 。 在你開始模擬之前,Mockito需要一個類的實例,而你沒有給自己創建一個測試實例的方法。
更一般地說,如Effective Java第 17項中所述, 您不應該從構造函數中調用可覆蓋的方法 。 例如,如果這樣做,您可以在子類中提供覆蓋,該子類引用final
字段但在final
字段設置之前運行。 它可能不會讓你遇到麻煩,但它在Java中是一個壞習慣。
幸運的是,您可以重構代碼以輕松完成此操作:
public class MyClass {
public MyClass() {
this(true);
}
/** For testing. */
MyClass(boolean runSetup) {
if (runSetup) {
setup();
}
}
/* ... */
}
為了使它更加明顯,您可以將單參數MyClass
構造函數MyClass
私有,並提供public static
工廠方法:
/* ... */
public static MyClass createForTesting() {
return new MyClass(false);
}
private MyClass(boolean runSetup) {
/* ... */
雖然一些開發人員認為在主要用於測試的方法中編寫任何代碼是一種不好的做法,但請記住,您負責代碼的設計,而測試是您絕對知道需要適應的少數消費者之一。 雖然在“生產”代碼中避免顯式測試設置仍然是一個好主意,但為了測試而創建額外的方法或重載通常會使您的代碼整體更清晰,並且可以大大提高您的測試覆蓋率和可讀性。
感謝您的建議,但有點過於復雜。
我最后通過擴展類並覆蓋我的setup方法來模擬該方法。 這樣默認構造函數不會調用它的setup實現,而是調用覆蓋的方法。
這是代碼:
// src/author/MyClass.java
public class MyClass {
public MyClass() {
setup();
}
protected void setup() {
throw new Exception("I hate unit testing !");
}
public boolean doesItWork() {
return true;
}
}
// test/author/MyClass.java
public class MyClassTest {
private class MockedMyClass extends MyClass {
@Override
protected void setup() {
}
}
private MyClass instance;
@Before
public void setUp() { // Not to be confusing with `MyClass#setup()`!
instance = new MockedMyClass();
}
@Test
public void test_doesItWork() {
assertTrue(instance.doesItWork());
}
}
如果您不希望MyTest的安裝方法被其他子類調用或覆蓋,除了您的測試(因為其他開發人員可能會使用安裝方法非常糟糕),只需將可見性更改為默認值,只有您的類才能夠呼叫設置。
如果有一種更簡單的方法,請回答這個問題,因為我不是100%滿意我的解決方案。
使用PowerMock。
導入庫后,將其設置為使用不能調用的實例方法操作要模擬的類。
像這樣:
@RunWith(PowerMockRunner.class)
@PrepareForTest({<Other classes>, Myclass.class})
像這樣:
suppress(method(Myclass.class, "setup"));
像這樣:
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
// code here
return null;
}
}).when(Myclass.class, "setup");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.