如何在Spring中用Mockito模拟自动连接的@Value字段?
我使用的是Spring3.1.4.RELEASE和Mockito1.9.5。在我的春季课程中,我有:如何在Spring中用Mockito模拟自动连接的@Value字段?,spring,mockito,autowired,value-initialization,Spring,Mockito,Autowired,Value Initialization,我使用的是Spring3.1.4.RELEASE和Mockito1.9.5。在我的春季课程中,我有: @Value("#{myProps['default.url']}") private String defaultUrl; @Value("#{myProps['default.password']}") private String defaultrPassword; // ... 从我的JUnit测试中,我目前设置如下: @RunWith(SpringJUnit4ClassRunne
@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的魔力,以避免对代码进行任何修改
来自的注释提供了一个示例:
使用ReflectionTestUtils.setField(bean,“fieldName”,“value”)在测试期间调用bean
方法之前
查看教程了解更多信息,尽管您可能不需要它,因为该方法非常容易使用
更新
自从Spring4.2.RC1引入以来,现在可以设置静态字段,而不必提供类的实例。请参阅部分文档并提交。您还可以将属性配置模拟到测试类中
@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字段。虽然接受的答案是正确的,但我始终需要一些时间来正确调用“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;
}
及
这样做:
public class Foo {
private String foo;
public Foo(@Value("${foo}") String foo) {
this.foo = foo;
}
}
及
这种方法的好处:1)我们可以在没有依赖容器的情况下实例化Foo类(它只是一个构造函数),2)我们没有将测试与实现细节耦合(反射使用字符串将我们绑定到字段名,如果我们更改字段名,这可能会导致问题).您可以使用此magic Spring测试注释:
@TestPropertySource(properties = { "my.spring.property=20" })
看
例如,这是测试类:
@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注入。不再思考:)
因此,您可以使用构造函数传递任何字符串:
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
private ClassABC classABC;
@Before
public void setUp() {
ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}
参考:只要有可能,我将字段可见性设置为package protected,以便可以从测试类访问它。我用番石榴的注释记录了这一点(以防下一个家伙想知道为什么它不是私人的)。这样我就不必依赖于字段的字符串名,所有内容都保持类型安全
我知道这违反了我们在学校学到的标准封装实践。但是,一旦团队中同意这样做,我发现这是最实用的解决方案。为了防止链接失效:使用ReflectionTestUtils.setField(bean,“fieldName”,“value”)在测试过程中调用bean
方法之前执行code>。这是模拟从属性文件检索的属性的好方法。@MichałStochmal,这样做将产生以下结果:field是私有的java.lang.IllegalStateException:无法访问方法:Class org.springframework.util.ReflectionUtils无法访问org.springframework.util.ReflectionUtils.HandlerReflectionException(ReflectionUtils.java:112)上带有修饰符“”的类com.kaleidofin.app.service.impl.CVLKRAProvider的成员在org.springframework.util.ReflectionUtils.setField(ReflectionUtils.java:655)中,缺点是:如果有人弄乱了注释,例如使用了属性“bar”而不是“foo”,那么测试仍然有效。我只是有这个例子。@NilsEl Himoud对于OP问题来说,这是一个公平的观点,但是您提出的问题对于使用反射utils vs构造函数并没有任何好处或坏处。这个答案的要点是考虑构造函数而不是反射util(公认的答案)。马克,谢谢你的回答,我很感激这个调整的简单和干净。这里一样…+1我也做了同样的调整,但仍然没有反映出这应该是公认的答案。请注意,我自己的参考:您需要模拟您正在使用的所有@value,您不能模拟第一个值,然后从属性中注入第二个值。如果每个测试需要不同的配置,有没有办法使用它?
MySpringClass MySpringClass = new MySpringClass("anyUrl", "anyPassword");
@InjectMocks
private ClassABC classABC;
@Before
public void setUp() {
ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}