模拟 final class 并将其注入自动装配的数据成员并克服 postConstruct 方法调用

Mock final class and inject it to autowired data member and overcome postConstruct method call

我想使用自动装配的最终 class 对象以及另一个具有 @PostConstruct 方法的自动装配的 class 对 java class 进行单元测试。虽然可以单独测试它们,但我无法将它们组合在一起。

这个问题是 injecting mockito mocks into spring bean

问题的延伸

待测代码

public class A { 
    @Autowired
    private FinalClass serviceClient;
    @Autowired
    private ClassWithPostConstructor resourceVerifier;

    //no setters or constructors

    public String useBothFinalClassAndClassWithPostConstructor() {
        //logic to be tested
    }
}

工作测试class

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class ATest {
    //@org.mockito.Mock //fails to mock final class
    @org.powermock.api.easymock.annotation.Mock
    private FinalClass serviceClient;
    @org.powermock.api.easymock.annotation.Mock
    private ClassWithPostConstructor resourceVerifier;
    //other mock objects required for mocking the services

    //@InjectMocks //fails since mocking final class
    private A a;

    @Before
    public void init() {
        a = new A();
        //working snippet with setters created in A and without @Autowired here within the test
        serviceClient = PowerMock.create(FinalClass.class);
        a.setServiceClient(serviceClient);
        resourceVerifier = PowerMock.create(ClassWithPostConstructor.class);
        a.setClassWithPostConstructor(resourceVerifier);
    }

    @Test
    public void testTheMethodUsingExpectAndVerify() {
        //test the functionality here
        EasyMock.expect(serviceClient.callService()).andReturn("someMock");
        EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
        PowerMock.replayAll();
        A.useBothFinalClassAndClassWithPostConstructor();
        PowerMock.verifyAll();
    }
}

以上代码适用于文件中设置器的需要

预期测试 class

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:spring-configuration/unit-testing-config.xml"})
@PrepareForTest(FinalClass.class)
public class ATest {
    @Autowired
    private FinalClass serviceClient;
    @Autowired
    private ClassWithPostConstructor resourceVerifier;
    //other mock objects required for mocking the services

    private A a;

    @Before
    public void init() {
        a = new A();
    }

    @Test
    public void testTheMethodUsingExpectAndVerify() {
        //test the functions here
        EasyMock.expect(serviceClient.callService()).andReturn("someMock");
        EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
        PowerMock.replayAll();
        A.useBothFinalClassAndClassWithPostConstructor();
        PowerMock.verifyAll();
    }
}

//spring-configuration/unit-testing-config.xml
//same error even on customer factory
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
    <constructor-arg type="java.lang.Class" value="com.company...resourceVerifier" /> 
</bean>
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
    <constructor-arg type="java.lang.Class" value="com.company...serviceClient" /> 
</bean>

上面的代码片段模拟了 finalClass 但调用了 ResourceVerifier.class 的 @PostConstructor - 这里应该做什么来克服这个调用。


调查

但其中 none 有效,因为用例是所有这些的组合。


可能的解决方案

  • 删除 @Autowired 字段并使用 Setter 注入 以便可以通过使用 PowerMock 进行正常模拟(已测试work) - 但这是一个约定俗成的外部团队包 - 我应该尽力坚持下去。
  • 或者将@Autowired 设置为 setter 或构造函数

备选方案?

我不认为 classes 需要重组,因为它们服务于它们的目的并且设计得很好。

  • 任何其他不需要保持对正在测试的 class 的手的方法 - 如果我没有修改此 class 的权限怎么办?即,纯测试库依赖解决方案。
  • 不确定 PowerMockito 是否可行?还没有尝试过 PowerMockito 与 PowerMock.
  • 的组合

嗯,

Not sure whether it is possible by PowerMockito? Haven't tried the combination of PowerMockito with PowerMock.

在我看来,您脑子里一片混乱,并且误解了 PowerMock/Mockito 和 EasyMock。

你不应该同时使用 PowerMockitoPowerMock,因为这两个 类 对 PowerMock 友好 API 用于两个不同的模拟框架:EasyMock and Mockito.并且没有理由同时使用它们。

当然这需要工作

//@org.mockito.Mock //fails to mock final class
@org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
@org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services

//@InjectMocks //fails since mocking final class
private A a;

因为,您通过 EasyMock API 减速并创建模拟,但尝试使用 Mockito Annotation 注入它。

您只需要选择一个 Mocking Framework 并使用合适的 API 即可。目前,Mockito + PowerMockito(PowerMock API for Mockito)更符合您的要求。

您可以完整示例它如何在 PowerMock github

上工作
@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class SpringInjectFinalClassExampleTest {

    @Mock
    private FinalClass finalClass;

    @InjectMocks
    private MyBean myBean = new MyBean();;

    @Test
    public void testInjectFinalClass() {
        final String value = "What's up?";
        when(finalClass.sayHello()).thenReturn(value);

        assertEquals(value, myBean.sayHello());

    }

}