[英]How do I get typeName() from a mock object in a JUnit test?
This is one of the strangest methods I've had to mock. 这是我不得不模拟的最奇怪的方法之一。 I need to somehow reconcile my unit test with the following code:
我需要以某种方式使我的单元测试与以下代码一致:
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();
}
}
It LOOKS like it might be a straightforward unit test (I moved getBeanInfo() to a separate method so I could mock it without tripping over Introspector). 它看起来像是一个简单的单元测试(我将getBeanInfo()移到了一个单独的方法,所以我可以模拟它而不用跳过Introspector)。 However, it will always throw an InvocationTargetException whenever I get to getTypeName().
但是,每当我到达getTypeName()时,它将始终抛出InvocationTargetException。 Is there a way to somehow mock a PropertyDescriptor's property type?
有没有办法模拟PropertyDescriptor的属性类型? I found a solution here on stackoverflow, but it hasn't helped much.
我在这里找到了关于stackoverflow的解决方案,但并没有太大帮助。
A strange generics edge case with Mockito.when() and generic type inference 带有Mockito.when()和通用类型推断的奇怪的通用边缘情况
Here's the code for how I mock the BenInfo object: 这是我如何模拟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.
}
The mockValue object is just a generic object. 模拟值对象只是一个通用对象。 The mockBeanInfo object is pretty self-explanatory.
mockBeanInfo对象是很容易解释的。 This code does work.
此代码可以正常工作。 The problem is mocking the PropertyDescriptor name.
问题是嘲笑PropertyDescriptor名称。
Here's getBeanInfo(): 这是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;
}
And finally mockBeanInfo: 最后是mockBeanInfo:
@Mock private java.beans.BeanInfo mockBeanInfo;
Let's talk about what a Java Bean is : 让我们谈谈什么是Java Bean :
- All properties private (use getters/setters )
所有私有属性(使用getters / setters )
- A public no-argument constructor
一个公开的无参数构造函数
- Implements
Serializable
.实现
Serializable
。
In other words, a Bean is just a data structure. 换句话说,Bean只是一种数据结构。 It has no behavior, no unintended consequences that you want to prevent from happening with a mock.
它没有任何行为,也没有想要防止发生的意外后果。 In other words, you shouldn't be mocking
BeanInfo
at all . 换句话说,你不应该嘲笑
BeanInfo
可言 。
However, you do want to ensure that your class is doing the right things with your BeanInfo objects. 但是,您确实要确保您的类对BeanInfo对象执行正确的操作。 You want to get real
BeanInfo
objects in both your production code and your test , because it's a data structure. 您想在生产代码和测试中都获得真实的
BeanInfo
对象,因为它是一个数据结构。 So, what you really need is a way to get access to these real BeanInfo
objects in your test method. 因此,您真正需要的是一种在测试方法中访问这些实际
BeanInfo
对象的方法。
Note: You're not going to be able to avoid using the real Introspector
here, because your application needs the data it provides. 注意:您将无法避免在这里使用真正的
Introspector
,因为您的应用程序需要它提供的数据。
Here's how I would fix your problem: 这是解决问题的方法:
Refactor your getBeanInfo()
behavior to use a separate class, BeanInfoProvider
: 重构
getBeanInfo()
行为以使用单独的类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; } }
Inject this behavior into ClientViewer
, probably by adding a constructor argument. 可以通过添加构造函数参数将此行为注入
ClientViewer
。
private final BeanInfoProvider provider; public ClientViewer(..., BeanInfoProvider provider) { // snip this.provider = provider; }
Change your methods that use BeanInfo
to use this BeanInfoProvider
更改使用
BeanInfo
的方法以使用此BeanInfoProvider
protected void sub(Object obj) { try { BeanInfo beanInfo = provider.getBeanInfo(obj); // snip
Make an implementation of BeanInfoProvider
that generates spies and allows you to access them. 实现
BeanInfoProvider
的实现,该实现生成间谍程序并允许您访问它们。 Note: you need to cache the BeanInfo
spy to make sure you get the same one inside ClientViewer
and your test method. 注意:您需要缓存
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); } } }
Inject this into your test 注入到您的测试中
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. }
This will allow you to have access to the BeanInfo
objects that get generated - because they are real data structures, implemented as partial mocks, and you won't get these InvocationTargetException
s anymore. 这将允许您访问所生成的
BeanInfo
对象-因为它们是作为部分模拟实现的真实数据结构,并且您将不再获得这些InvocationTargetException
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.