【问题标题】:How to use @Values fields in @Rule in JUnit?如何在 JUnit 的 @Rule 中使用 @Values 字段?
【发布时间】:2019-06-19 11:57:51
【问题描述】:

如何在junit @Rule 定义中使用@Value spring 注入属性?

背景:我想使用 FakeSftpServerRule 创建一个带有嵌入式内存 sftp 服务器的 junit 测试。

问题:@Rule@Value 字段被注入之前执行。

@Value("${ftp.port}")
private Integer port;

@Value("${ftp.user}")
private String user;

@Value("${ftp.pass}")
private String pass;

@Rule
public final FakeSftpServerRule sftpServer = new FakeSftpServerRule().setPort(port).addUser(user, pass);

【问题讨论】:

标签: java spring junit spring-boot-test


【解决方案1】:

解决您的问题的一个解决方案是初始化 Spring 上下文并自己执行属性查找,如我最初的评论和说明该概念的 referencing to a sample 所示。

现在的问题是,如果您不小心,您最终可能会初始化 Spring 上下文两次,一次使用静态定义,一次使用常规 Spring 测试设置。

幸运的是,Spring 本身带有一个AbstractJUnit4SpringContextTests 支持类,允许您重用已加载的 Spring ApplicationContext。

我们现在可以利用此功能来初始化假 SFTP 服务器,但也允许将 bean 注入测试代码,而无需通过 context.getBean(...) 调用手动查找。

下面的代码应该让您了解如何做到这一点:

@ContextConfiguration(classes = SpringTest.Config.class)
public class SpringTest extends AbstractJUnit4SpringContextTests {

  @Configuration
  @PropertySource("classpath:some.properties")
  static class Config {
    @Bean(name = "namedBean")
    String someBean() {
      return "Test";
    }

    @Bean
    UUID uuidGenerator() {
      return UUID.randomUUID();
    }
  }

  static ApplicationContext context;
  static int port;
  static String user;
  static String pass;

  static {
    context = new AnnotationConfigApplicationContext(SpringTest.Config.class);
    Environment env = context.getBean(Environment.class);
    String sPort = env.getProperty("port");
    port = Integer.parseInt(sPort);
    user = env.getProperty("user");
    pass = env.getProperty("pass");
  }

  @Autowired
  private UUID uuidGenerator;

  public SpringTest() {
    // reuse the already initialized Spring application context
    setApplicationContext(context);
  }

  @Rule
  public final FakeSftpServerRule sftpServer = 
          new FakeSftpServerRule().setPort(port).addUser(user, pass);

  @Test
  public void test() {
    String someBean = context.getBean("namedBean", String.class);
    System.out.println(someBean);

    System.out.println(uuidGenearator.toString());
    System.out.println(sftpServer.getPort());
  }
}

一种可能更优雅的方式是在 Spring 配置中定义类似这样的内容:

@Configuration
@PropertySource("classpath:some.properties")
@EnableConfigurationProperties(SftpSettings.class)
static class Config {
  ...
}

其中SftpSettings 是一个简单的bean 类,例如

@Validated
@Getter
@Setter
@ConfigurationProperties(prefix="sftp")
public class SftpSettings {
  @NotNull
  private int port;
  @NotNull
  private String user;
  @NotNull
  private String pass;
}

然后在 SftpSettings 而不是 Environment 上执行查找:

static {
  context = new AnnotationConfigApplicationContext(SpringTest.Config.class);
  SftpSettings settings = context.getBean(SftpSettings.class);
  port = settings.getPort();
  user = settings.getUser();
  pass = settings.getPass();
}

这样,Spring 将负责从属性文件中查找值并将这些值转换为适当的格式。

【讨论】:

    【解决方案2】:

    您可以在方法上使用@Rule 注解,该注解在值注入后执行。至少SpringJUnit4ClassRunner似乎是这样。

    @Value("${ftp.port}")
    private Integer port;
    
    @Value("${ftp.user}")
    private String user;
    
    @Value("${ftp.pass}")
    private String pass;
    
    @Rule
    public FakeSftpServerRule getFakeSftpServerRule() {
        return new FakeSftpServerRule().setPort(port).addUser(user, pass);
    }
    

    请注意,如果您的规则同时实现了TestRuleMethodRule 接口,则该方法可能会被拾取并执行两次,这可能会导致一些问题。为避免这种情况,请将方法的返回类型更改为 TestRule

    @Rule
    public TestRule getFakeSftpServerRule() {
        return new FakeSftpServerRule().setPort(port).addUser(user, pass);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-28
      • 2017-04-28
      • 2018-12-03
      • 2012-12-29
      • 1970-01-01
      相关资源
      最近更新 更多