单元测试中如何mock用@Value注解注入的属性 置顶!
实际项目开发中,我们经常会使用@Value注解从配置文件中注入属性值,写单元测试时,在不启动容器的条件下,如何对这种属性进行mock呢?
对这种情况,Spring提供了一个很好的工具类ReflectionTestUtils
来实现。
注入属性:
@Value("${option.switch}")
private String switchValue;
对这个属性,在单测中可以这样mock:
ReflectionTestUtils.setField(targetObject, "switchValue", "turn on");
顾名思义,该工具类就是利用反射机制来实现的,底层源码如图所示:
/**
* Set the {@linkplain Field field} with the given {@code name}/{@code type}
* on the provided {@code targetObject}/{@code targetClass} to the supplied
* {@code value}.
* <p>If the supplied {@code targetObject} is a <em>proxy</em>, it will
* be {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing
* the field to be set on the ultimate target of the proxy.
* <p>This method traverses the class hierarchy in search of the desired
* field. In addition, an attempt will be made to make non-{@code public}
* fields <em>accessible</em>, thus allowing one to set {@code protected},
* {@code private}, and <em>package-private</em> fields.
* @param targetObject the target object on which to set the field; may be
* {@code null} if the field is static
* @param targetClass the target class on which to set the field; may
* be {@code null} if the field is an instance field
* @param name the name of the field to set; may be {@code null} if
* {@code type} is specified
* @param value the value to set
* @param type the type of the field to set; may be {@code null} if
* {@code name} is specified
* @since 4.2
* @see ReflectionUtils#findField(Class, String, Class)
* @see ReflectionUtils#makeAccessible(Field)
* @see ReflectionUtils#setField(Field, Object, Object)
* @see AopTestUtils#getUltimateTargetObject(Object)
*/
public static void setField(Object targetObject, Class<?> targetClass, String name, Object value, Class<?> type) {
Assert.isTrue(targetObject != null || targetClass != null,
"Either targetObject or targetClass for the field must be specified");
if (targetObject != null && springAopPresent) {
targetObject = AopTestUtils.getUltimateTargetObject(targetObject);
}
if (targetClass == null) {
targetClass = targetObject.getClass();
}
Field field = ReflectionUtils.findField(targetClass, name, type);
if (field == null) {
throw new IllegalArgumentException(String.format(
"Could not find field '%s' of type [%s] on %s or target class [%s]", name, type,
safeToString(targetObject), targetClass));
}
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Setting field '%s' of type [%s] on %s or target class [%s] to value [%s]", name, type,
safeToString(targetObject), targetClass, value));
}
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, targetObject, value);
}
/**
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
* supplied {@code name} and/or {@link Class type}. Searches all superclasses
* up to {@link Object}.
* @param clazz the class to introspect
* @param name the name of the field (may be {@code null} if type is specified)
* @param type the type of the field (may be {@code null} if name is specified)
* @return the corresponding Field object, or {@code null} if not found
*/
public static Field findField(Class<?> clazz, String name, Class<?> type) {
Assert.notNull(clazz, "Class must not be null");
Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified");
Class<?> searchType = clazz;
while (Object.class != searchType && searchType != null) {
Field[] fields = getDeclaredFields(searchType);
for (Field field : fields) {
if ((name == null || name.equals(field.getName())) &&
(type == null || type.equals(field.getType()))) {
return field;
}
}
searchType = searchType.getSuperclass();
}
return null;
}
具体可参见:How do I mock an autowired @Value field in Spring with Mockito? - Stack Overflow
标题:单元测试中如何mock用@Value注解注入的属性
作者:jerrycookie
地址:https://www.mmzsblog.cn/articles/2023/07/10/1688967983371.html
如未加特殊说明,文章均为原创,转载必须注明出处。均采用CC BY-SA 4.0 协议!
本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。若本站转载文章遗漏了原文链接,请及时告知,我们将做删除处理!文章观点不代表本网站立场,如需处理请联系首页客服。• 网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
• 公众号转载请联系网站首页的微信号申请白名单!