如何从 JUnit 测试中的模拟对象获取 typeName()?
How do I get typeName() from a mock object in a JUnit test?
这是我不得不嘲笑的最奇怪的方法之一。我需要以某种方式协调我的单元测试与以下代码:
protected void sub(Object obj) {
try {
BeanInfo beanInfo = getBeanInfo(obj);
for (PropertyDescriptor pb : beanInfo.getPropertyDescriptors()) {
String fieldType = pd.getPropertyType.getTypeName();
System.out.println(fieldType);
}
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
看起来它可能是一个简单的单元测试(我将 getBeanInfo() 移到了一个单独的方法中,这样我就可以模拟它而不会被 Introspector 绊倒)。但是,每当我到达 getTypeName() 时,它总是会抛出 InvocationTargetException。有没有办法以某种方式模拟 PropertyDescriptor 的 属性 类型?我在 Whosebug 上找到了一个解决方案,但没有太大帮助。
A strange generics edge case with Mockito.when() and generic type inference
这是我如何模拟 BenInfo 对象的代码:
@Test
public void testSub() {
ClientViewer cv = mock(ClientViewer.class); // The class that I'm testing.
when(cv.getBeanInfo(mockValue)).thenReturn(mockBeanInfo);
// Rest of the test.
}
mockValue 对象只是一个通用对象。 mockBeanInfo 对象是不言自明的。此代码确实有效。问题是模拟 PropertyDescriptor 名称。
这里是 getBeanInfo():
protected BeanInfo getBeanInfo(Object obj) {
BeanInfo beanInfo = null;
try {
Class cls = obj.getClas();
beanInfo = Introspector.getBeanInfo(cls);
} catch (IntrospectionException e) {
e.printStackTrace();
}
return beanInfo;
}
最后是 mockBeanInfo:
@Mock private java.beans.BeanInfo mockBeanInfo;
Let's talk about what a Java Bean is:
- All properties private (use getters/setters)
- A public no-argument constructor
- Implements
Serializable
.
换句话说,Bean只是一种数据结构。它没有任何行为,也没有您想通过模拟防止发生的意外后果。换句话说,你根本不应该嘲笑 BeanInfo
.
但是,您确实希望确保 class 对 BeanInfo 对象做正确的事情。您希望在 生产代码和测试 中都获得 real BeanInfo
对象,因为它是一种数据结构。因此,您真正需要的是一种在测试方法中访问这些真实 BeanInfo
对象的方法。
注意:您将无法避免在此处使用真正的 Introspector
,因为您的应用程序需要它提供的数据。
以下是我将如何解决您的问题:
重构您的 getBeanInfo()
行为以使用单独的 class、BeanInfoProvider
:
public class SimpleBeanInfoProvider implements BeanInfoProvider {
public BeanInfo getBeanInfo(Object obj) {
BeanInfo beanInfo = null;
try {
Class cls = obj.getClass();
beanInfo = Introspector.getBeanInfo(cls);
} catch (IntrospectionException e) {
e.printStackTrace();
}
return beanInfo;
}
}
可能通过添加构造函数参数将此行为注入 ClientViewer
。
private final BeanInfoProvider provider;
public ClientViewer(..., BeanInfoProvider provider) {
// snip
this.provider = provider;
}
将使用 BeanInfo
的方法更改为使用此 BeanInfoProvider
protected void sub(Object obj) {
try {
BeanInfo beanInfo = provider.getBeanInfo(obj);
// snip
实现 BeanInfoProvider
生成 spies 并允许您访问它们。注意:您需要缓存 BeanInfo
间谍以确保您在 ClientViewer
和您的测试方法中获得相同的间谍。
public class SpyBeanInfoProvider implements BeanInfoProvider {
private final BeanInfoProvider delegate;
private final Map<Class<?>, BeanInfo> spyMap = new HashMap<>();
public SpyBeanInfoProvider(BeanInfoProvider delegate) {
this.delegate = delegate;
}
@Override
public BeanInfo getBeanInfo(Object obj) {
Class<?> klass = obj.getClass();
if(!spyMap.containsKey(klass)) {
BeanInfo info = spy(delegate.getBeanInfo(obj));
spyMap.put(klass, info);
return info;
} else {
return spyMap.get(obj);
}
}
}
将其注入到您的测试中
private BeanInfoProvider makeBeanInfoProvider() {
return new SpyBeanInfoProvider(new IntrospectorBeanInfoProvider());
}
@Test
public void testSub() {
BeanInfoProvider provider = makeBeanInfoProvider();
ClientViewer viewer = new ClientViewer(makeBeanInfoProvider());
viewer.sub(obj);
BeanInfo spy = provider.getBeanInfo(obj);
// Now do your test
verify(spy).getPropertyDescriptors();
// etc.
}
这将允许您访问生成的 BeanInfo
对象 - 因为它们是真实的数据结构,实现为部分模拟,您将不会再获得这些 InvocationTargetException
.
这是我不得不嘲笑的最奇怪的方法之一。我需要以某种方式协调我的单元测试与以下代码:
protected void sub(Object obj) {
try {
BeanInfo beanInfo = getBeanInfo(obj);
for (PropertyDescriptor pb : beanInfo.getPropertyDescriptors()) {
String fieldType = pd.getPropertyType.getTypeName();
System.out.println(fieldType);
}
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
看起来它可能是一个简单的单元测试(我将 getBeanInfo() 移到了一个单独的方法中,这样我就可以模拟它而不会被 Introspector 绊倒)。但是,每当我到达 getTypeName() 时,它总是会抛出 InvocationTargetException。有没有办法以某种方式模拟 PropertyDescriptor 的 属性 类型?我在 Whosebug 上找到了一个解决方案,但没有太大帮助。
A strange generics edge case with Mockito.when() and generic type inference
这是我如何模拟 BenInfo 对象的代码:
@Test
public void testSub() {
ClientViewer cv = mock(ClientViewer.class); // The class that I'm testing.
when(cv.getBeanInfo(mockValue)).thenReturn(mockBeanInfo);
// Rest of the test.
}
mockValue 对象只是一个通用对象。 mockBeanInfo 对象是不言自明的。此代码确实有效。问题是模拟 PropertyDescriptor 名称。
这里是 getBeanInfo():
protected BeanInfo getBeanInfo(Object obj) {
BeanInfo beanInfo = null;
try {
Class cls = obj.getClas();
beanInfo = Introspector.getBeanInfo(cls);
} catch (IntrospectionException e) {
e.printStackTrace();
}
return beanInfo;
}
最后是 mockBeanInfo:
@Mock private java.beans.BeanInfo mockBeanInfo;
Let's talk about what a Java Bean is:
- All properties private (use getters/setters)
- A public no-argument constructor
- Implements
Serializable
.
换句话说,Bean只是一种数据结构。它没有任何行为,也没有您想通过模拟防止发生的意外后果。换句话说,你根本不应该嘲笑 BeanInfo
.
但是,您确实希望确保 class 对 BeanInfo 对象做正确的事情。您希望在 生产代码和测试 中都获得 real BeanInfo
对象,因为它是一种数据结构。因此,您真正需要的是一种在测试方法中访问这些真实 BeanInfo
对象的方法。
注意:您将无法避免在此处使用真正的 Introspector
,因为您的应用程序需要它提供的数据。
以下是我将如何解决您的问题:
重构您的
getBeanInfo()
行为以使用单独的 class、BeanInfoProvider
:public class SimpleBeanInfoProvider implements BeanInfoProvider { public BeanInfo getBeanInfo(Object obj) { BeanInfo beanInfo = null; try { Class cls = obj.getClass(); beanInfo = Introspector.getBeanInfo(cls); } catch (IntrospectionException e) { e.printStackTrace(); } return beanInfo; } }
可能通过添加构造函数参数将此行为注入
ClientViewer
。private final BeanInfoProvider provider; public ClientViewer(..., BeanInfoProvider provider) { // snip this.provider = provider; }
将使用
BeanInfo
的方法更改为使用此BeanInfoProvider
protected void sub(Object obj) { try { BeanInfo beanInfo = provider.getBeanInfo(obj); // snip
实现
BeanInfoProvider
生成 spies 并允许您访问它们。注意:您需要缓存BeanInfo
间谍以确保您在ClientViewer
和您的测试方法中获得相同的间谍。public class SpyBeanInfoProvider implements BeanInfoProvider { private final BeanInfoProvider delegate; private final Map<Class<?>, BeanInfo> spyMap = new HashMap<>(); public SpyBeanInfoProvider(BeanInfoProvider delegate) { this.delegate = delegate; } @Override public BeanInfo getBeanInfo(Object obj) { Class<?> klass = obj.getClass(); if(!spyMap.containsKey(klass)) { BeanInfo info = spy(delegate.getBeanInfo(obj)); spyMap.put(klass, info); return info; } else { return spyMap.get(obj); } } }
将其注入到您的测试中
private BeanInfoProvider makeBeanInfoProvider() { return new SpyBeanInfoProvider(new IntrospectorBeanInfoProvider()); } @Test public void testSub() { BeanInfoProvider provider = makeBeanInfoProvider(); ClientViewer viewer = new ClientViewer(makeBeanInfoProvider()); viewer.sub(obj); BeanInfo spy = provider.getBeanInfo(obj); // Now do your test verify(spy).getPropertyDescriptors(); // etc. }
这将允许您访问生成的 BeanInfo
对象 - 因为它们是真实的数据结构,实现为部分模拟,您将不会再获得这些 InvocationTargetException
.