Java 如果没有load()方法,我无法测试singleton

Java 如果没有load()方法,我无法测试singleton,java,unit-testing,testing,design-patterns,singleton,Java,Unit Testing,Testing,Design Patterns,Singleton,启动应用程序时,我需要从不同的源加载属性:war、文件系统、数据库和JVM。我需要加载一次属性,并在运行应用程序时使用它们。我不需要刷新它。我没有DI——它是一个简单的java应用程序,带有单例。我决定在启动应用程序时创建AppProperties单例并加载属性。这是目前对我来说最好的解决方案(我希望有人能做出最好的解决方案)。这是我的单身汉: import java.io.InputStream; import java.util.Properties; public class AppPr

启动应用程序时,我需要从不同的源加载属性:war、文件系统、数据库和JVM。我需要加载一次属性,并在运行应用程序时使用它们。我不需要刷新它。我没有DI——它是一个简单的java应用程序,带有单例。我决定在启动应用程序时创建AppProperties单例并加载属性。这是目前对我来说最好的解决方案(我希望有人能做出最好的解决方案)。这是我的单身汉:

import java.io.InputStream;
import java.util.Properties;

public class AppProperties {
    private static AppProperties instance;

    private Properties propertiesFromWar;
    private Properties propertiesFromFile;
    private Properties propertiesFromDB;

    private AppProperties() {
        propertiesFromWar = new Properties();
        try {
            propertiesFromWar.load(getPropertiesAsInputStreamFromWar());
            propertiesFromFile.load(getPropertiesAsInputStreamFromFile());
            propertiesFromDB.load(getPropertiesAsInputStreamFromDB());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private InputStream getPropertiesAsInputStreamFromDB() {
        //some implementation
        return null;
    }

    private InputStream getPropertiesAsInputStreamFromFile() {
        //some implementation
        return null;
    }

    private InputStream getPropertiesAsInputStreamFromWar() {
        return getClass().getResourceAsStream("META-INF/application.properties");
    }

    public static AppProperties getInstance() {
        if (instance == null) {
            instance = new AppProperties();
        }
        return instance;
    }

    public String getProperty(String key) {
        String value;
        value = System.getProperty(key);
        if (value == null) {
            value = propertiesFromDB.getProperty(key);
            if (value == null) {
                value = propertiesFromFile.getProperty(key);
                if (value == null) {
                    value = propertiesFromWar.getProperty(key);
                }
            }
        }
        return value;
    }
}
但我不明白,我如何在测试中使用它。因为我硬编码了aplication.properties文件的路径。当我在测试中创建这个实例时,我将使用真实属性创建AppProperties

我尝试添加一个公共方法,如
load(filePath)
。但有了这个方法,它将不再是一个单例。如果有人在另一个应用程序中调用此方法,我的单例将重新加载新数据。现在我有两个问题

  • 如果我添加load()方法,则重新加载数据会很危险。但我可以在测试中使用它
  • 如果我不添加此方法-我无法测试它 我读了这篇文章


    但有些时候我不明白。如果我有带私有构造函数的singleton,我不能像本文中那样扩展它。

    因为测试允许指定JVM启动参数,所以这可以很容易地“解决”。
    这也增加了一些灵活性

    java -DpropertiesPath="..." -jar yourJar.jar
    
    并且,修改您的代码

    private InputStream getPropertiesAsInputStreamFromWar(){
    最后一个字符串propertiesPath=Objects.requirennull(System.getProperty(“propertiesPath”);
    返回getClass().getResourceAsStream(propertiesPath);
    }
    
    您可以使用默认值,例如

    META-INF/application.properties

    在测试资源目录中,创建
    META-INF
    目录。这里创建一个文件
    application.properties
    ,并在其中添加一些用于测试的属性


    运行测试时,请确保上面的目录位于类路径中。这样,当调用
    getPropertiesAsInputStreamFromWar()
    时,它将在类路径中查找
    META-INF/application.properties

    如果我是你,我就不会在这里用单身汉。这是一种被过度使用的模式,类似于
    SomeClass.getInstance()
    的代码使得测试比应该的要困难得多。如果代码很难进行单元测试,可能应该对其进行重构。将
    Singelton
    用作概念,而不是语言模板。