简体   繁体   English

如何从JUnit测试中的模拟对象获取typeName()?

[英]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

  1. All properties private (use getters/setters ) 所有私有属性(使用getters / setters
  2. A public no-argument constructor 一个公开的无参数构造函数
  3. 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: 这是解决问题的方法:

  1. 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; } } 
  2. Inject this behavior into ClientViewer , probably by adding a constructor argument. 可以通过添加构造函数参数将此行为注入ClientViewer

     private final BeanInfoProvider provider; public ClientViewer(..., BeanInfoProvider provider) { // snip this.provider = provider; } 
  3. Change your methods that use BeanInfo to use this BeanInfoProvider 更改使用BeanInfo的方法以使用此BeanInfoProvider

     protected void sub(Object obj) { try { BeanInfo beanInfo = provider.getBeanInfo(obj); // snip 
  4. 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); } } } 
  5. 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.

相关问题 如何获取测试类实例JUnit从ClassRunner内部运行测试用例? - How do I get the test class instance JUnit is running test cases on from inside the ClassRunner? 如何在JUnit 5测试中从请求的InvocationBuilder对象获取标头 - How to get header from InvocationBuilder object from request in JUnit 5 test 使用 Guice,如何将单元测试中的模拟对象注入正在测试的类中 - Using Guice, how do I inject a mock object from my unit test, into the class being tested 如何从TestNG获得一份关于所有测试用例的junit报告? - How do I get one junit report from TestNG for all my test cases? 如何让Spring MVC在JUnit测试中调用验证? - How do I get Spring MVC to invoke validation in a JUnit test? 如何在JUnit测试中测试列表? - How do I test a list in a JUnit test? 如何从 JUnit 测试用例中的接口获取 class 实例 - How to i get class instance from interface in JUnit test case JUnit测试-在模拟对象上资产或验证(或两者)? - JUnit test - asset or verify (or both) on a mock object? 模拟 WebApplicationContext 以在过滤器 junit 测试中获取属性 - Mock WebApplicationContext to get property in Filter junit test JUnit如何测试构造函数? - How do I JUnit test a constructor?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM