java - 使用可注入(inject)单例

标签 java unit-testing dependency-injection junit mocking

我最近偶然发现了这个 interesting concept这可能会节省我很多测试工作。 我不明白的是如何在运行时注入(inject)提供程序?

场景很简单:我正在使用我选择的模拟框架在运行时构造一个模拟对象,但我事先不知道生成的类的名称,因为它是一个模拟(所以我无法配置提前,我不想)。

有人在单元测试中成功使用过这种技术吗?

谢谢。

最佳答案

那篇文章中描述的概念是一个 Ambient Context使用 Service Locator在后台。

由于使用了静态属性和Service Locator的使用,这种模式对于单元测试来说非常不方便。为了能够运行验证使用此单例的代码的测试,您需要设置一个有效的服务定位器并使用您关心的单例(可能是模拟实例)配置它使用测试。

即使是文章给出的例子也已经存在这些问题,因为“你喜欢单例吗?”代码,很难测试:

if (DialogDisplayer.getDefault().yesOrNo(
    "Do you like singletons?"
)) {
    System.err.println("OK, thank you!");
} else {
    System.err.println(
        "Visit http://singletons.apidesign.org to"
        + " change your mind!"
    );
}

更好的选择是使用构造函数注入(inject)来注入(inject)该单例(请原谅我的法语,但我不是母语为 Java 的人):

public class AskTheUserController
{
    private DialogDisplayer dialogDisplayer;
    private MessageDisplayer messageDisplayer;

    public AskTheUserController(DialogDisplayer dialogDisplayer,
        MessageDisplayer messageDisplayer)
    {
        this.dialogDisplayer = dialogDisplayer;
        this.messageDisplayer = messageDisplayer;
    }

    public void AskTheUser()
    {
        if (this.dialogDisplayer.yesOrNo(
            "Do you like singletons?"
        )) {
            this.messageDisplayer.display("OK, thank you!");
        } else {
            this.messageDisplayer.display(
                "Visit http://singletons.apidesign.org to"
                + " change your mind!"
            );
        }
    }
}

该代码中还有另一个“隐藏”依赖项:System.err.println。它使用 MessageDisplayer 接口(interface)进行了抽象。这段代码有几个明显的优点:

  • 通过注入(inject)两个依赖项,消费者甚至不需要知道这些依赖项是单例。
  • 代码清楚地传达了它所采用的依赖关系。
  • 可以使用模拟对象轻松测试代码。
  • 测试代码不需要配置服务定位器。

您的测试可能如下所示:

@Test
public void AskTheUser_WhenUserSaysYes_WeThankHim()
{
    // Arrange
    bool answer = true;

    MockMessageDisplayer message = new MockMessageDisplayer();
    MockDialogDisplayer dialog = new MockDialogDisplayer(answer);

    AskTheUserController controller =
        new AskTheUserController(dialog, message);

    // Act
    controller.AskTheUser();

    // Assert
    Assert.AreEqual("OK, thank you!", message.displayedMessage);
}

@Test
public void AskTheUser_WhenUserSaysNo_WeLetHimChangeHisMind()
{
    // Arrange
    bool answer = true;

    MockMessageDisplayer message = new MockMessageDisplayer();
    MockDialogDisplayer dialog = new MockDialogDisplayer(answer);

    AskTheUserController controller =
        new AskTheUserController(dialog, message);

    // Act
    controller.AskTheUser();

    // Assert
    Assert.IsTrue(
        message.displayedMessage.contains("change your mind"));
}

当您使用文章中所示的“可注入(inject)单例”模式时,您的测试代码将永远不会像上面的代码那样有意揭示。

关于java - 使用可注入(inject)单例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11933813/

相关文章:

java - 如何修复 JavaFX 中的 NoClassDefFoundError?

java - 如果检查的字符串仍然有剩余字符,如何使正则表达式匹配失败?

entity-framework - 对 3 层架构中的数据访问层进行单元测试

javascript - Angular 2注入(inject)全局依赖

c# - .NET Core 依赖注入(inject)——强制立即激活

java - 通过构造函数注入(inject)在其他 bean 中连接条件 bean

java - Java中实例变量的初始值

java - 如何将长值转换为字符串集

swift - 单元测试中的竞争条件

python - PyCharm 运行选择单元测试