簡體   English   中英

如何模擬我無法在測試中實例化的對象?

[英]How do I mock objects that I can't instantiate in my tests?

我在我的測試中使用EasyMock來模擬對象。 但是,我如何模擬在我的代碼中的其他位置創建的對象? 查看以下psudo代碼。 我想模擬WebService#getPersonById,我該怎么做?

public class Person {
  public Person find(int id) {
    WebService ws = new WebService();
    return ws.getPersonById(id);
  }
}

public class PersonTest {
  testFind() {
    // How do I mock WebService#getPersonById here?
  }
}

如果您使用控制和依賴注入的反轉來連接您的服務,模擬通常很有效。 所以你的人應該是這樣的

public class Person() {
  WebService ws = null;

  // or use setters instead of constructor injection
  Persion(WebService ws) {
     this.ws = ws;
  }
  public Person find(int id) {
    return ws.getPersonById(id);
  }
}

希望很明顯,通過這個更改,您現在可以為WebService創建一個模擬和模擬控件,並將其插入到您的測試中,因為當您創建要測試的Person時,您可以將模擬傳遞給構造函數(或setter)如果你去那條路)

在您的真實環境中,IoC容器將注入真正的Web服務。

現在,如果你不想處理所有這些IoC的東西,你需要做的就是將你的web服務與你的Person分開(應該調用PersonService或者其他東西,而不僅僅是Person,它表示實體)。 換句話說,編寫代碼的方式只能使用一種類型的WebService。 你需要這樣做,所以Person只需要某種類型的WebService,而不是你硬編碼的特定類型。

最后,在編寫的代碼中,WebService是一個類,而不是一個接口。 WebService應該是一個接口,您應該進行某種實現。 EasyMock適用於界面; 它可能能夠模擬具體的類(自從我實際使用它以來已經有一段時間了),但作為一個設計原則,你應該指定所需的接口,而不是具體的類。

使用EasyMock(或大多數其他模擬API)無法做到這一點。 另一方面,使用JMockit,這樣的測試將非常簡單和優雅:

public class PersonTest
{
    @Test
    public testFind(@Mocked final WebService ws) {
        final int id = 123;

        new NonStrictExpectations() {{
            ws.getPersonById(id); result = new Person(id);
        }};

        Person personFound = new Person().find(id);

        assertEquals(id, personFound.getId());
    }
}

因此,每當我們遇到無法首先編寫單元測試的情況時,我們就不能自動斷定測試中的代碼是不可測試的並且需要重構。 有時會出現這種情況,但肯定並非總是如此。 也許問題不在於測試中的代碼,而是在使用的特定模擬工具的限制中。

我想你錯過了一個更大的問題。 測試的難點在於試圖告訴你一些事情,即擁有一個Person對象(域的一部分)也使用遠程服務來查找自身的更多實例(系統的一部分)正在混淆問題。 Person的獲取與Person對象分開,最終會得到更干凈,更便攜的代碼。

不要混淆立即的便利性(我手上有一個Person對象,所以我會用它來獲得更多)具有可維護性。

您可以嘗試使用PowerMock whenNew方法

https://github.com/jayway/powermock/wiki/MockitoUsage#how-to-mock-construction-of-new-objects

更多例子 - http://www.programcreek.com/java-api-examples/index.php?api=org.powermock.api.mockito.PowerMockito

你的代碼可能看起來像 -

whenNew(WebService.class).withAnyArguments().thenReturn(yourMockInstanceToWebServiceClass);

首先你需要通過注入來使ws成為模擬。

public abstract class Person() {
  public Person find(int id) {
    WebService ws = createWebService();
    return ws.getPersonById(id);
  }
  protected abstract WebService createWebService();
}

然后你可以將它放入並使用EasyMock.expect來設置返回

public class PersonTest() {
  testFind() {
    WebService mock = EasyMock.createMock(WebService.class);
    Person p = new Persion() {
      protected WebService createWebService() {
        return mock;
      }
    }
    EasyMock.expect(mock.getPersonById()).andReturn(dummyValue);
    //Test code
  }
}

你還需要一個PersonImpl來擁有真正的create方法。

暫無
暫無

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

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