Java 多个包裹对象中的反射

Java 多个包裹对象中的反射,java,reflection,junit,mockito,transactional,Java,Reflection,Junit,Mockito,Transactional,在JUnit测试中,我想更改SpringDAO中的hibernate模板。这把刀是 使用@Transactional进行注释,以便在运行时和 被Mockitos spy()方法监视。所以刀会被那个间谍第二次包起来 因此DAO现在有两个包装对象:一个来自@Transactional,一个来自spy。由于不知道哪些包装器是首先创建的,因此我无法通过反射在DAO中设置hibernate模板 如何在双包装DAO中设置模板 [编辑] 一些资料来源: /** * This class gets wrappe

在JUnit测试中,我想更改SpringDAO中的hibernate模板。这把刀是

  • 使用@Transactional进行注释,以便在运行时和
  • 被Mockitos spy()方法监视。所以刀会被那个间谍第二次包起来
  • 因此DAO现在有两个包装对象:一个来自@Transactional,一个来自spy。由于不知道哪些包装器是首先创建的,因此我无法通过反射在DAO中设置hibernate模板

    如何在双包装DAO中设置模板

    [编辑]

    一些资料来源:

    /**
    * This class gets wrapped by a proxy object because of @Transactional.
    */
    @Transactional 
    public class MyDao implements SomeDaoInterface { ... }
    
    在测试类中:

    public class MyTestClass {
    @Autowired 
    private MyDao myDao;
    
    @Test
    public void myTestMethod() throws Exception {
       final MyDao daoSpy = spy(myDao);   // Dao gets wrapped with second wrapper
    
       final Field field = MyDao.class.getDeclaredField("template");
       field.setAccessible(true);
       field.set(daoSpy, mySpecialMockedTemplate);  // ERROR: want to inject the template but
                                                    // dont know in which wrapper
    }
    }
    

    调用setter方法,而不是访问字段

    调用setter方法,而不是访问字段

    看来您的反射代码是错误的。请使用以下语句:

    field.set(daoSpy, mySpecialMockedTemplate);
    
    然而,看看您的测试代码,您似乎正在使用Spring创建
    MyDao
    实例。使用反射来设置模板似乎有点奇怪,为什么不在Spring中配置它呢? 或者甚至使用一个实际的setter?或者使字段受保护,这样只有单元测试才能访问它

    编辑:关于注入,您可以在测试中创建DAO实例,并让Mockito注入您的
    SpecialLockedTemplate
    。你可以这样写:

    @RunWith(MockitoJUnitRunner.class)
    public class MyTestClass {
        @InjectMocks private MyDao dao;
    
        @Mock SpecialTemplate specialTemplate;
    
        @Test void dao_should_call_the_template_with_parameter_A_and_B() {
            // given
    
            // when
            dao.someCall("A", "B");
    
            // then
            verify(specialTemplate).someCallWith("A", "B");
        }
    }
    

    不过有一些警告,如果可能的话,避免部分嘲弄(使用间谍)。避免使用你不拥有的类型,你应该阅读这篇文章,为什么这是一个坏主意。

    看来你的反射代码是错误的。请使用以下语句:

    field.set(daoSpy, mySpecialMockedTemplate);
    
    然而,看看您的测试代码,您似乎正在使用Spring创建
    MyDao
    实例。使用反射来设置模板似乎有点奇怪,为什么不在Spring中配置它呢? 或者甚至使用一个实际的setter?或者使字段受保护,这样只有单元测试才能访问它

    编辑:关于注入,您可以在测试中创建DAO实例,并让Mockito注入您的
    SpecialLockedTemplate
    。你可以这样写:

    @RunWith(MockitoJUnitRunner.class)
    public class MyTestClass {
        @InjectMocks private MyDao dao;
    
        @Mock SpecialTemplate specialTemplate;
    
        @Test void dao_should_call_the_template_with_parameter_A_and_B() {
            // given
    
            // when
            dao.someCall("A", "B");
    
            // then
            verify(specialTemplate).someCallWith("A", "B");
        }
    }
    

    不过有一些警告,如果可能的话,避免部分嘲弄(使用间谍)。避免使用您不拥有的类型,您应该阅读这篇文章,为什么这是一个坏主意。

    Hi,您可以添加一些示例代码吗?Hi,您可以添加一些示例代码吗?PowerMockito在这里可能会有所帮助,还有一个选项。虽然PowerMock库对于难以测试的代码非常有用,我强烈建议避免使用需要测试Powermock的设计代码。这是一条黑客之路,代码/测试很难维护。对,我发布了一个错误的反射代码。使用setter并不能解决问题:Spring因为@Autowired而对dao进行包装。setter将把任何给定的属性设置到这个包装中,而不是dao。在Spring中进行配置实际上似乎是实现这一点的唯一方法。或者,dao可以被实例化而不是自动连接。Mockito提供了一个轻型自动注入实用程序,我编辑了这方面的答案。PowerMockito在这里可能会有所帮助,因为还有另一个选项。虽然PowerMock库对于难以测试的代码非常有用,但我强烈建议避免需要测试PowerMock的设计代码。这是一条黑客之路,代码/测试很难维护。对,我发布了一个错误的反射代码。使用setter并不能解决问题:Spring因为@Autowired而对dao进行包装。setter将把任何给定的属性设置到这个包装中,而不是dao。在Spring中进行配置实际上似乎是实现这一点的唯一方法。或者dao可以被实例化而不是自动连接。Mockito提供了一个轻型自动注入实用程序,我在这方面编辑了我的答案。使用setter并不能解决问题:Spring因为@autowired而包装dao。setter会将任何给定的属性设置到此包装中,而不是dao。使用setter并不能解决问题:Spring会因为@Autowired而包装dao。setter将把任何给定的属性设置到这个包装中,而不是dao。