Spring单元测试中填充@Value注解属性

Spring单元测试中填充@Value注解属性

技术背景

在Spring项目开发中,经常会使用@Value注解从配置文件中注入属性值。在编写单元测试时,有时不希望依赖配置文件,因为配置文件中的值变化可能影响测试结果,而测试的重点是验证代码逻辑,并非属性值本身。因此,需要一种在单元测试中填充@Value注解属性的方法。

实现步骤

方法一:使用ReflectionTestUtils

不使用Spring上下文,在测试中直接创建类的实例,利用ReflectionTestUtilssetField方法设置私有字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.test.util.ReflectionTestUtils;

// 被测试的类
@Component
public class MyBean {
@Value("${this.property.value}")
private String thisProperty;

public String getThisProperty() {
return thisProperty;
}
}

// 测试类
public class MyBeanTest {
@Test
public void testMyBean() {
MyBean myBean = new MyBean();
ReflectionTestUtils.setField(myBean, "thisProperty", "testValue");
assertEquals("testValue", myBean.getThisProperty());
}
}

方法二:使用@TestPropertySource注解

从Spring 4.1开始,可以在单元测试类上使用@TestPropertySource注解设置属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertEquals;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FooTest.Config.class)
@TestPropertySource(properties = {
"some.bar.value=testValue"
})
public class FooTest {

@Value("${some.bar.value}")
String bar;

@Test
public void testValueSetup() {
assertEquals("testValue", bar);
}

@Configuration
static class Config {
@Bean
public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
}

方法三:通过构造函数注入

@Value注解的属性作为构造函数的参数,使类可以在不依赖Spring的情况下进行单元测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 被测试的类
@Component
public class Foo {
private String property;

public Foo(@Value("${property.value}") String property) {
this.property = property;
}

public String getProperty() {
return property;
}
}

// 单元测试类
public class FooTest {
@Test
public void testFoo() {
Foo foo = new Foo("dummyValue");
assertEquals("dummyValue", foo.getProperty());
}
}

// 集成测试类
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertEquals;

@SpringBootTest(properties = "property.value=dummyValue")
@RunWith(SpringJUnit4ClassRunner.class)
public class FooIntegrationTest {
@Autowired
Foo foo;

@Test
public void testFooIntegration() {
assertEquals("dummyValue", foo.getProperty());
}
}

核心代码

以下是使用ReflectionTestUtils的核心代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.test.util.ReflectionTestUtils;

// 被测试的类
public class MyClass {
@Value("${my.property}")
private String myProperty;

public String getMyProperty() {
return myProperty;
}
}

// 测试类
public class MyClassTest {
@Test
public void testMyClass() {
MyClass myClass = new MyClass();
ReflectionTestUtils.setField(myClass, "myProperty", "testValue");
assertEquals("testValue", myClass.getMyProperty());
}
}

最佳实践

  • 优先考虑构造函数注入:将@Value注解的属性作为构造函数的参数,使类更易于测试,同时提高代码的可维护性。
  • 避免滥用反射:反射虽然可以解决问题,但会带来运行时错误的风险,且会降低代码的可读性和可维护性。
  • 使用@TestPropertySource:在需要使用Spring上下文的测试中,使用@TestPropertySource注解设置属性值,避免依赖外部配置文件。

常见问题

  • 反射在Java 17及以上版本可能出错:由于Java 17及以上版本对反射的限制更严格,使用ReflectionTestUtils可能会出现错误。可以考虑其他方法,如构造函数注入。
  • @Value注解无法解析占位符:在测试中可能会出现@Value注解无法解析占位符的问题,需要确保Spring上下文中有PropertySourcesPlaceholderConfigurer实例。

Spring单元测试中填充@Value注解属性
https://119291.xyz/posts/2025-04-22.spring-unit-test-populating-value/
作者
ww
发布于
2025年4月22日
许可协议