[英]Spring value injection in mockito
我正在尝试为以下方法编写测试类
public class CustomServiceImpl implements CustomService {
@Value("#{myProp['custom.url']}")
private String url;
@Autowire
private DataService dataService;
我在类中的方法之一中使用注入的 url 值。 为了测试这个,我写了一个junit类
@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public CustomServiceTest{
private CustomService customService;
@Mock
private DataService dataService;
@Before
public void setup() {
customService = new CustomServiceImpl();
Setter.set(customService, "dataService", dataService);
}
...
}
public class Setter {
public static void set(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
在 applicationContext-test.xml 我正在使用加载属性文件
<util:properties id="myProp" location="myProp.properties"/>
但是在运行测试时, url 值没有加载到 CustomService 中。 我想知道是否有办法完成这项工作。
谢谢
import org.springframework.test.util.ReflectionTestUtils;
@RunWith(MockitoJUnitRunner.class)
public CustomServiceTest{
@InjectMocks
private CustomServiceImpl customService;
@Mock
private DataService dataService;
@Before
public void setup() {
ReflectionTestUtils.setField(customService, "url", "http://someurl");
}
...
}
我同意@skaffman 的评论。
除了您的测试使用MockitoJUnitRunner
,因此它不会寻找任何 Spring 的东西,这唯一的目的是初始化 Mockito 模拟。 ContextConfiguration
不足以用 spring 连接东西。 从技术上讲,如果您想要与弹簧相关的东西,您可以使用 JUnit 的以下运行程序: SpringJUnit4ClassRunner
。
此外,在编写单元测试时,您可能需要重新考虑使用 spring。 在单元测试中使用弹簧接线是错误的。 但是,如果您要编写集成测试,那么为什么要在那里使用 Mockito,这没有意义(如 skaffman 所说)!
编辑:现在在你的代码中你直接在你的 before 块中设置CustomerServiceImpl
,这也没有意义。 那里根本不涉及春天!
@Before
public void setup() {
customService = new CustomServiceImpl();
Setter.set(customService, "dataService", dataService);
}
编辑 2:如果你想编写CustomerServiceImpl
的单元测试,那么避免使用 Spring 的东西并直接注入属性的值。 您也可以使用 Mockito 将DataService
模拟直接注入到测试实例中。
@RunWith(MockitoJUnitRunner.class)
public CustomServiceImplTest{
@InjectMocks private CustomServiceImpl customServiceImpl;
@Mock private DataService dataService;
@Before void inject_url() { customServiceImpl.url = "http://..."; }
@Test public void customerService_should_delegate_to_dataService() { ... }
}
您可能已经注意到,我正在使用对url
字段的直接访问,该字段可以是包可见的。 这是一种实际注入 URL 值的测试解决方法,因为 Mockito 仅注入模拟。
您可以自动连接到一个 mutator(setter),而不仅仅是注释私有字段。 然后你也可以从你的测试类中使用那个 setter。 无需公开,包私有即可,因为 Spring 仍然可以访问它,否则只有您的测试可以进入(或同一包中的其他代码)。
@Value("#{myProp['custom.url']}")
String setUrl( final String url ) {
this.url = url;
}
我不喜欢仅仅为了测试而以不同的方式自动装配(与我的代码库相比),但是从测试中更改被测类的替代方法简直是邪恶的。
我有一个从属性文件中读取的字符串列表。 @Before 块中使用的 ReflectionTestUtils 类 setField 方法帮助我在测试执行之前设置了这些值。 它甚至适用于我的依赖于 Common DaoSupport 类的 dao 层。
@Before
public void setList() {
List<String> mockedList = new ArrayList<>();
mockedSimList.add("CMS");
mockedSimList.add("SDP");
ReflectionTestUtils.setField(mockedController, "ActualListInController",
mockedList);
}
你不应该嘲笑你试图测试的东西。 这是毫无意义的,因为您不会触及您尝试测试的任何代码。 而是从上下文中获取CustomerServiceImpl
的实例。
您可以使用这个小实用程序类 ( gist ) 将字段值自动注入目标类:
public class ValueInjectionUtils {
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
private static final ConversionService CONVERSION_SERVICE = new DefaultConversionService();
private static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER =
new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, SystemPropertyUtils.PLACEHOLDER_SUFFIX,
SystemPropertyUtils.VALUE_SEPARATOR, true);
public static void injectFieldValues(Object testClassInstance, Properties properties) {
for (Field field : FieldUtils.getFieldsListWithAnnotation(testClassInstance.getClass(), Value.class)) {
String value = field.getAnnotation(Value.class).value();
if (value != null) {
try {
Object resolvedValue = resolveValue(value, properties);
FieldUtils.writeField(field, testClassInstance, CONVERSION_SERVICE.convert(resolvedValue, field.getType()),
true);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
}
private static Object resolveValue(String value, Properties properties) {
String replacedPlaceholderString = PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(value, properties);
return evaluateSpEL(replacedPlaceholderString, properties);
}
private static Object evaluateSpEL(String value, Properties properties) {
Expression expression = EXPRESSION_PARSER.parseExpression(value, new TemplateParserContext());
EvaluationContext context =
SimpleEvaluationContext.forPropertyAccessors(new MapAccessor()).withRootObject(properties).build();
return expression.getValue(context);
}
}
它使用org.apache.commons.lang3.reflect.FieldUtils
访问所有用@Value
注释的字段,然后使用 Spring 实用程序类来解析所有占位符值。 您还可以更改参数的类型properties
,以PlaceholderResolver
你喜欢用自己的PlaceholderResolver情况。 在您的测试中,您可以使用它来注入一组作为Map
或Properties
实例给出的值,如下例所示:
HashMap<String, Object> props = new HashMap<>();
props.put("custom.url", "http://some.url");
Properties properties = new Properties();
properties.put("myProp", props);
ValueInjectionUtils.injectFieldValues(testTarget, properties);
这将尝试解析dataService
所有@Value
注释字段。 我个人更喜欢这个解决方案而不是ReflectionTestUtils.setField(dataService, "field", "value");
因为您不必依赖硬编码的字段名称。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.