簡體   English   中英

如何模擬通過 Class.newInstance(className) 創建的對象?

[英]How to mock an object created via Class.newInstance(className)?

我正在嘗試將單元測試添加到一些遺留代碼中,這些代碼具有傳遞給它的 String 類名,並創建一個使用Class.newInstance(String className)實現特定處理程序接口的對象。 我可以控制我傳遞的類名,我可以獲得指向新處理程序對象的指針(通過getHandler()調用),並且我想使用 Mockito 觀察對它的調用。

我目前的解決方案是:

  1. 創建一個實現該接口的新測試類TestHandler
  2. 讓該測試類包含一個 Mockito 模擬對象,該對象也實現了該接口。
  3. 手動將所有接口方法傳遞給模擬對象。
  4. 通過getMock()方法使模擬對象可訪問。
  5. 通過對objectUnderTest.getHandler().getMock()進行 verify() 調用來觀察對象。

這有效,但感覺有點不雅,尤其是必須手動編寫所有傳遞方法。

有更好的解決方案嗎?

從根本上說,您遇到了與嘗試使用new測試新創建的實例相同的問題; Class.newInstance (可能正確Class.forName(foo).newInstance() )不會傷害您,但也不會幫助您。

作為旁注,您的TestHandler聽起來像是一個通用的委托實現,無論如何聽起來都非常有用(特別是如果您需要編寫 Handler 包裝器)。 如果是,您可能希望將其提升為與生產代碼樹中的 Handler 相鄰。

雖然我承認您提到了遺留代碼,但如果允許您重構以包含測試接縫,這將變得非常容易。 (為了便於解釋,這里忽略反射異常。)

public ReturnType yourMethodUnderTest(String className) {
  return yourMethodUnderTest(Class.newInstance(className));
}

/** Package private for testing. */
public ReturnType yourMethodUnderTest(Handler handler) {
  return yourMethodUnderTest(Class.newInstance(className));
}

您還可以提取對象創建並在測試中替換它:

/** Instance field, package-private to replace in tests. */
Function<String, Handler> instanceCreator =
    ( x -> (Handler) Class.forName(x).newInstance());

public ReturnType yourMethodUnderTest(String className) {
  Handler handler = instanceCreator.apply(className);
  // ...
}

你甚至可以把它提取到一個方法中並在你的測試中替換它:

public ReturnType yourMethodUnderTest(String className) {
  Handler handler = createHandler(className);
  // ...
}

/** Package private for testing. */
Handler createHandler(String className) {
  return Class.forName(className).newInstance();
}

@Test public void yourTest() {
  // Manually replace createHandler. You could also use a Mockito spy here.
  ObjectUnderTest objectUnderTest = new ObjectUnderTest() {
    @Override Handler createHandler(String className) {
      return mock(Handler.class);
    }
  }
  // ...
}

旁注:即使 Mockito 創建了一個命名的動態類型,您幾乎肯定無法破解它並允許您的代碼按名稱創建它。 這是因為對mock的調用在 Mockito 的內部 state 中注冊了實例

// BAD: Unlikely to work
@Test public void yourTest() {
  objectUnderTest.methodUnderTest(
      mock(Handler.class).getClass().getName());
  // ...
}

創建一個公共方法,您將在其中放置獲取類的 newInstance 的邏輯

ClassA objectClassA=createNewInstance(className);

同樣,並且

public ClassA createInstance(String className){
 return (ClassA) (Class.forName(className)).newInstance();
}

現在假設我們在 ClassB 的內部創建了一個 classA 的實例,然后在 B 的 TestClass 中,我們可以簡單地模擬這個 createInstance 方法

doReturn(mockClassA).when(mockClassB).createInstance(className);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM