使用 java 个所有者 aeonbits 进行测试

Testing with java owner aeonbits

我一直在使用 java OWNER 进行基于 属性 的配置。

我创建了一个静态方法

public static final ApplicationConfiguration config = ConfigFactory.create(ApplicationConfiguration.class,
        System.getProperties(), System.getenv());

并且我在需要 conf 的任何地方导入 class。

不用说,单元测试就是一个PITA。我找不到覆盖配置中值的好方法。

我想避免在每个 class 中将配置作为依赖项传递。 它增加了很多冗长,从设计的角度来看没有意义。 同样适用于在每个 class

中调用配置工厂
ApplicationConfiguration config = ConfigFactory.create(ApplicationConfiguration.class,
        System.getProperties(), System.getenv());

你有什么建议吗?有最佳实践吗?

两件事:

  1. 您可以创建一个 class 来提供属性并且所有用户都使用它:

    public class PropertiesAccessor {
    
       private static MyConfiguration mMyConfig = ConfigFactory.create(MyConfiguration.class);
    
       private PropertiesAccessor() 
          // No need to allow instantiation of this class
       }
    
       /**
        * Get properties for this application
        *
        * @return Properties
        */
       public static MyConfiguration getProperties() {
          return mMyConfig;
       }
    
       // for unit testing
       @VisibleForTesting
       public static void setProperties(MyConfiguration config) {
          mMyConfig = config;
       }
    }
    

    现在,任何需要 属性 的地方都可以使用这个静态方法

    PropertiesAccessor.getProperties()
    
  2. 注意有一个测试方法,setProperties()。有多种使用此方法的方法。您可以创建一个测试 属性 文件,加载它,然后调用 setProperties() 方法。我喜欢这样的实用方法:

    public static void initProperties(String fileName) {    
    
       Properties testProperties = new Properties();    
    
       File file = new File(fileName);
       FileInputStream stream = null;   
       try {    
          stream = new FileInputStream(file);   
          testProperties.load(stream);  
       } catch (IOException e) {    
          // Log or whatever you want to do
       } finally {  
          if (stream != null) {
          try { 
             stream.close();    
       } catch (IOException e) {    
         // Log or whatever you want to do; 
       }    
    
       MyConfiguration config = ConfigFactory.create(MyConfiguration.class, testProperties);    
       PropertiesAccessor.setProperties(config);
    }
    

    然后,您可以拥有各种属性文件。

    或者,如果您只想设置一些属性,请执行以下操作:

     Properties testProperties = new Properties();
     testProperties.setProperty("key1", "data1");
     testProperties.setProperty("key2", "data2");
     final MyConfiguration myConfig = ConfigFactory.create(MyConfiguration.class, testProperties);
     PropertiesAccessor.setProperties(myConfig);
    

Needless to say, unit testing is a PITA. I couldn't find a good way of override the values in the configuration.

您可以有 Mutable 个配置对象。阅读手册时没有 PITA。

但还有更好的方法。

当您知道 SOLID principles

时,接口非常容易测试和处理。
import static org.mockito.Mockito.*;

MyConfig cfg = mock(MyConfig.class); // mock object
when(cfg.myConfigurationThing()).thenReturn("whateverYourObjectNeeds");

ObjectThatYouNeedToTest targetObject = 
    new ObjectThatYouNeedToTest(cfg); // Dependency Injection 
                                   // see: http://wiki.c2.com/?ConstructorInjection

assertEquals("expected result", targetObject.whateverYouNeedToTest());

// then you can also verify interactions:

verify(cfg, times(1)).myConfigurationThing();

上面的代码片段显示了一个 Mockito 示例,但是还有很多类似的测试框架。

检查什么Mock Objects are, what is Dependency Injection and Interface Segregation

I would like to avoid passing the config as a dependency in every class.

如果您的对象需要配置,您应该将配置传递给您的对象。事情本该如此。 如果它很冗长,则说明您的应用程序设计中有些地方需要修改。例如,您选择在 class 中有一个带有静态成员的单例,这就是 not very good,特别是用于测试。

Same applies for calling the config factory in every class

在每个 class 中调用配置工厂,这是个坏主意。也许您可以将您的配置文件拆分为许多特定于组件的配置文件。那是一种方式(我的方式)。

但是如果你需要一个巨大的配置文件(这不是我最喜欢的方法),你的配置接口不需要遵循相同的结构:你可以有多个组件配置接口从同一个文件中读取:如果你使用热重载,这不是最好的,我可能可以做更多的事情来模块化嵌套配置接口树中的配置对象。
但是,嘿,我是在业余时间自己做的,而且我免费分享了它;如果人们做事的方式与我不同,他们需要我支持他们的工作方式,也许他们可以支持开发,贡献好的代码,或者雇用我的时间来改进它。

对不起 PITA。