簡體   English   中英

靜態工廠方法和模擬

[英]Static factory methods and mocking

你如何使用靜態工廠方法和模擬協調?

很多人只會說:不要使用靜態工廠方法,而是使用DI。

好吧,有時你無法避免靜態工廠方法。 考慮以下用例,應該是熟悉的:

想象一下,你有一個名為Option的類,就像在scala中一樣。 如果要為所有缺失值重用相同的實例,則無法避免使用靜態工廠方法。

一旦你去new Option(null)你創建一個新的選項對象,你就不能一遍又一遍地返回同一個對象。

類似的用例是Integer.valueOf() ,它將重用128以下的值的整數對象。不使用靜態工廠方法是不可能的。

另一個優點是工廠方法比new關鍵字更具描述性。

那么你們如何處理必須使用靜態工廠方法並同時想要使用繼承和模擬?

謝謝。

使用PowerMock可以模擬靜態方法。 從他們的Wiki頁面考慮以下示例:

@Test
public void testRegisterService() throws Exception {
    long expectedId = 42;

    // We create a new instance of test class under test as usually.
    ServiceRegistartor tested = new ServiceRegistartor();

    // This is the way to tell PowerMock to mock all static methods of a
    // given class
    mockStatic(IdGenerator.class);

    /*
     * The static method call to IdGenerator.generateNewId() expectation.
     * This is why we need PowerMock.
     */
    expect(IdGenerator.generateNewId()).andReturn(expectedId);

    // Note how we replay the class, not the instance!
    replay(IdGenerator.class);

    long actualId = tested.registerService(new Object());

    // Note how we verify the class, not the instance!
    verify(IdGenerator.class);

    // Assert that the ID is correct
    assertEquals(expectedId, actualId);
}

甚至可以模擬出一種特定方法,並使用部分模擬將其余方法保留原樣。

既然這是一個理論問題,我會提出一個理論上的答案。 工廠范式是另一種理論的建立點:注射。 如果在需要時注入您創建的對象,那么您只需要注入模擬對象來執行所有測試。 有很多好的書籍/網頁可以幫助你開始這個。

我的第一個選擇是避免模擬任何東西的需要,所以使用靜態工廠方法或不是沒有區別。

也就是說,如果我確實想要或需要嘲笑它們,那么我就是這樣做的。 例如,假設您正在測試基於JSF的Web應用程序,並且您想要模擬javax.faces.context.FacesContext對象。 我會在測試中使用JMockit庫(我碰巧開發)編寫以下代碼:

@Test
public void exampleTest(@Mocked final FacesContext ctx) {
    // Call the code under test, which will at some point
    // call FacesContext.getCurrentInstance(), then add an
    // error message for display in the web page.

    new Verifications() {{
        FacesMessage msg;
        ctx.addMessage(null, msg = withCapture());

        assertEquals("The expected error message.", msg.getSummary());
        assertNotNull(msg.getDetail());
    }};
}

在此示例中, Faces.getCurrentInstance()是靜態工廠方法,一旦模擬了類,它將自動返回模擬FacesContext實例。

我們簡單地避免使用靜態工廠方法,而是使用依賴注入。

如果java從一開始就設計了DI,那么Integer.valueOf()就是:

  • integerType.valueOf()其中integerType是注入的依賴項,或

  • typeSystem.getInteger().valueOf()其中typeSystem將是一個注入的依賴項,或

  • environment.getTypeSystem().getInteger().getFactory()其中environment是一個注入依賴項。

靜態工廠無法做任何事情,而勤奮使用依賴注入則無法做到這一點。

現在,一旦有人通過靜態工廠方法提供某些東西,他們實際上就是在強迫你采取靜態道路。 這很不幸。 但是您仍然可以將靜態內容包裝在您自己的設備的實例中,然后將這些實例作為依賴項注入到生產代碼中,然后讓您的單元測試運行這些實例,從而避免像模擬靜態方法那樣執行此類不合理的操作。

例如,您可以在實現某些Console接口的某個StandardConsole對象中包裝System.out ,並將該Console接口作為依賴項注入您的應用程序。

(如果你這樣做,我甚至會補充說你可以繼續並配置你的版本控制系統來拒絕任何提交包含字符串“System.out”的代碼的嘗試。[evil grin])

暫無
暫無

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

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