我正在使用 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
的魔力来避免对您的代码进行任何修改。
Michał Stochmal 的评论提供了一个示例:
使用 ReflectionTestUtils.setField(bean, "fieldName", "value");在测试期间调用您的 bean 方法之前。
查看 this 教程以获得更多信息,尽管您可能不需要它,因为该方法非常易于使用
更新
由于 Spring 4.2.RC1 的引入,现在可以设置静态字段而无需提供类的实例。请参阅文档的 this 部分和 this 提交。
这是我第三次用谷歌搜索这个 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")
您可以使用这个神奇的 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;
...
您还可以将您的属性配置模拟到您的测试类中
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
@Configuration
public static class MockConfig{
@Bean
public Properties myProps(){
Properties properties = new Properties();
properties.setProperty("default.url", "myUrl");
properties.setProperty("property.value2", "value2");
return properties;
}
}
@Value("#{myProps['default.url']}")
private String defaultUrl;
@Test
public void testValue(){
Assert.assertEquals("myUrl", defaultUrl);
}
}
我想提出一个相关的解决方案,即将带有 @Value
注释的字段作为参数传递给构造函数,而不是使用 ReflectionTestUtils
类。
而不是这个:
public class Foo {
@Value("${foo}")
private String foo;
}
和
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;
}
}
和
public class FooTest {
private Foo foo;
@Before
public void setUp() {
foo = new Foo("foo");
}
@Test
public void testFoo() {
// stuff
}
}
这种方法的好处:1)我们可以在没有依赖容器的情况下实例化 Foo 类(它只是一个构造函数),2)我们不会将测试与实现细节耦合(反射使用字符串将我们与字段名称联系起来,如果我们更改字段名称可能会导致问题)。
我使用了下面的代码,它对我有用:
@InjectMocks
private ClassABC classABC;
@Before
public void setUp() {
ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}
参考:https://www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/
另请注意,我的班级中没有明确的“setter”方法(例如 setDefaultUrl),我不想仅仅为了测试目的而创建任何方法。
解决此问题的一种方法是将您的类更改为使用构造函数注入,它可用于测试和 Spring 注入。没有更多的反思:)
因此,您可以使用构造函数传递任何字符串:
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");
@InjectMocks
作为实践,所以我现在在使用 Kotlin 和 @Value
构造函数时遇到问题.但感谢分享这个答案。
只要有可能,我将字段可见性设置为包保护,以便可以从测试类访问它。我使用 Guava 的 @VisibleForTesting 注释记录了这一点(以防下一个人想知道为什么它不是私有的)。这样我就不必依赖字段的字符串名称,并且一切都保持类型安全。
我知道这违背了我们在学校教过的标准封装实践。但是一旦团队中达成了一些共识,我就发现这是最务实的解决方案。
不定期副业成功案例分享
bean
方法之前使用ReflectionTestUtils.setField(bean, "fieldName", "value");
。private
变量的@Value("${property.name}")
注释顶部来测试已访问属性的类时,这可以正常工作。@Value("#{${patientTypes}}") private Map<String, Integer> patientTypes;
?