繁体   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