我使用的是Spring 3.1.4。RELEASE和Mockito 1.9.5。在春季课上,我有:
@Value("#{myProps['default.url']}")
private String defaultUrl;
@Value("#{myProps['default.password']}")
private String defaultrPassword;
// ...
在我的JUnit测试中,我目前设置如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
我想模拟我的“defaultUrl”字段的值。注意,我不想模拟其他字段的值——我希望保持它们的原样,只有“defaultUrl”字段。还要注意,在我的类中没有显式的“setter”方法(例如setDefaultUrl),我不想仅仅为了测试的目的而创建任何方法。
鉴于此,我如何模拟一个值的一个字段?
您可以使用Spring的ReflectionTestUtils的魔力。setField,以避免对代码做任何修改。
michaovich randommal的评论提供了一个例子:
使用ReflectionTestUtils。setField(bean, "fieldName", "value");在测试期间调用bean方法之前。
查看本教程以获取更多信息,尽管您可能不需要它,因为该方法非常易于使用
更新
自Spring 4.2引入以来。RC1现在可以设置一个静态字段,而不必提供类的实例。请参阅文档的这一部分和这个提交。
这是我第三次在谷歌上搜索这篇SO帖子,因为我总是忘记如何模拟@Value字段。虽然接受的答案是正确的,我总是需要一些时间来获得“setField”调用正确,所以至少对我自己,我粘贴了一个示例代码片段在这里:
生产类:
@Value("#{myProps[‘some.default.url']}")
private String defaultUrl;
测试类:
import org.springframework.test.util.ReflectionTestUtils;
ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}",
// but simply the FIELDs name ("defaultUrl")
我想建议一个相关的解决方案,即将带@ value注释的字段作为参数传递给构造函数,而不是使用ReflectionTestUtils类。
而不是这样:
public class Foo {
@Value("${foo}")
private String foo;
}
and
public class FooTest {
@InjectMocks
private Foo foo;
@Before
public void setUp() {
ReflectionTestUtils.setField(Foo.class, "foo", "foo");
}
@Test
public void testFoo() {
// stuff
}
}
这样做:
public class Foo {
private String foo;
public Foo(@Value("${foo}") String foo) {
this.foo = foo;
}
}
and
public class FooTest {
private Foo foo;
@Before
public void setUp() {
foo = new Foo("foo");
}
@Test
public void testFoo() {
// stuff
}
}
这种方法的好处:1)我们可以实例化Foo类而不需要依赖容器(它只是一个构造函数),2)我们没有将我们的测试与我们的实现细节耦合(反射使用字符串将我们绑定到字段名,如果我们更改字段名,这可能会导致问题)。
您可以使用这个神奇的Spring Test注释:
@TestPropertySource(properties = { "my.spring.property=20" })
看到
org.springframework.test.context.TestPropertySource
例如,这是测试类:
@ContextConfiguration(classes = { MyTestClass.Config.class })
@TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {
public static class Config {
@Bean
MyClass getMyClass() {
return new MyClass ();
}
}
@Resource
private MyClass myClass ;
@Test
public void myTest() {
...
这是带有属性的类:
@Component
public class MyClass {
@Value("${my.spring.property}")
private int mySpringProperty;
...
还要注意,在我的类中没有显式的“setter”方法(例如setDefaultUrl),我不想仅仅为了测试的目的而创建任何方法。
解决这个问题的一种方法是将你的类改为使用构造函数注入,它可以用于测试和Spring注入。没有更多的思考:)
因此,你可以使用构造函数传递任何String:
class MySpringClass {
private final String defaultUrl;
private final String defaultrPassword;
public MySpringClass (
@Value("#{myProps['default.url']}") String defaultUrl,
@Value("#{myProps['default.password']}") String defaultrPassword) {
this.defaultUrl = defaultUrl;
this.defaultrPassword= defaultrPassword;
}
}
在你的测试中,使用它:
MySpringClass MySpringClass = new MySpringClass("anyUrl", "anyPassword");
另一种方法是使用@SpringBootTest注释属性字段。
这里我们重写了示例。firstProperty属性:
@SpringBootTest(properties = { "example.firstProperty=annotation" })
public class SpringBootPropertySourceResolverIntegrationTest {
@Autowired private PropertySourceResolver propertySourceResolver;
@Test
public void shouldSpringBootTestAnnotation_overridePropertyValues() {
String firstProperty = propertySourceResolver.getFirstProperty();
String secondProperty = propertySourceResolver.getSecondProperty();
Assert.assertEquals("annotation", firstProperty);
Assert.assertEquals("defaultSecond", secondProperty);
}
}
如您所见,它只覆盖一个属性。@SpringBootTest中没有提到的属性保持不变。因此,当我们只需要覆盖测试的特定属性时,这是一个很好的解决方案。
对于单个属性,可以不带大括号:
@SpringBootTest(properties = "example.firstProperty=annotation")
答案来自:https://www.baeldung.com/spring-tests-override-properties#springBootTest
我还鼓励您尽可能在构造函数中传递property作为参数,如Dherik answer (https://stackoverflow.com/a/52955459/1673775),因为它使您能够在单元测试中轻松模拟属性。
然而,在集成测试中,你通常不会手动创建对象,但是:
你使用@Autowired
您希望修改集成测试中间接使用的类中使用的属性,因为它是一些直接使用的类的深层依赖项。
那么这个@SpringBootTest的解决方案可能会有帮助。