如何在JUnit的@Rule中使用Spring @Value注入的属性?
解决@Rule初始化早于@Value注入的问题
这个坑我之前踩过!JUnit的@Rule是在测试类刚实例化的时候就创建了,而Spring的@Value注入要等到Spring上下文初始化完成后才会给字段赋值,所以你直接在@Rule的声明里用port、user这些字段,它们当时都是null,自然会出问题。下面给你几个简单可行的解决办法:
方案一:延迟SFTP服务器配置到@Before方法
这是改动最小的方案,先创建一个空的FakeSftpServerRule,等Spring完成注入后,再在@Before方法里设置参数:
@Value("${ftp.port}") private Integer port; @Value("${ftp.user}") private String user; @Value("${ftp.pass}") private String pass; // 先初始化一个空的Rule @Rule public FakeSftpServerRule sftpServer = new FakeSftpServerRule(); @Before public void setupSftpServer() { // 此时@Value已经完成注入,能拿到正确的配置值 sftpServer.setPort(port) .addUser(user, pass); }
方案二:自定义Spring感知的Rule
如果你的测试场景比较复杂,或者想把SFTP服务器的配置逻辑封装起来,可以自定义一个实现TestRule的类,让它能被Spring注入参数:
public class SpringAwareFakeSftpServerRule implements TestRule { private final FakeSftpServerRule delegate = new FakeSftpServerRule(); private Integer port; private String user; private String pass; @Autowired public void setPort(@Value("${ftp.port}") Integer port) { this.port = port; } @Autowired public void setUser(@Value("${ftp.user}") String user) { this.user = user; } @Autowired public void setPass(@Value("${ftp.pass}") String pass) { this.pass = pass; } @Override public Statement apply(Statement base, Description description) { // 在执行测试前完成SFTP服务器的配置 delegate.setPort(port).addUser(user, pass); return delegate.apply(base, description); } }
然后在测试类里这样用:
@Rule public SpringAwareFakeSftpServerRule sftpServer = new SpringAwareFakeSftpServerRule(); // 需要添加Spring的Rule来支持依赖注入 @ClassRule public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule();
为什么原来的代码会失败?
再给你理清楚执行顺序:
- JUnit实例化测试类,此时会创建
@Rule标注的sftpServer对象,这时候port、user、pass还没被Spring注入,都是null - Spring初始化上下文,完成
@Value字段的注入 - 执行测试方法
所以原来的代码相当于用null去设置SFTP服务器的端口和用户,肯定会出问题。
内容的提问来源于stack exchange,提问作者membersound
相关产品推荐
相关产品推荐

