1

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

这个问题是对将 mockito 模拟注入 spring bean的问题的扩展

要测试的代码

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

    //no setters or constructors

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

工作测试班

@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();
    }
}

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

预期测试类

@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 - 这里应该做些什么来克服这个调用。


调查

  • 可以使用@InjectMocks测试自动装配的文件,而无需弹簧上下文配置。
  • @InjectMock对于静态和最终字段静默失败,当失败时,它也不会注入其他模拟。
  • 可以使用PowerMock的 createMock 模拟最终类,并使用PowerMockRunner@PrepareForTest运行测试。但这需要新的不必要的设置器来为@Autowired字段注入模拟
  • MockitoAnnotations.@Mock不能很好地与 PowerMock 配合使用(尤其是在模拟最终类对象时),可以通过EasyMock.Annotations.@Mock解决
  • EasyMockPowerMock没有@InjectMocks注释来通过 Mockito 尽可能地注入模拟(将在几秒钟内解决问题)。
  • 可以通过SpringJUnit4Runner和单独的单元测试@ContextConfiguration注入自动装配的 spring bean
  • 可以使用PowerMockRunnerDelegate使用PowerMockRunnerSpringJUnit4Runner运行相同的测试文件
  • 我知道@PostConstruct方法如果在代码中模拟而不是通过使用spring bean 创建和注入将不会自动执行。
  • 如果编写了包装器工厂 bean 类并用于创建模拟,它会自动注入,但同时调用 @PostConstruct 方法。
  • 不可能依赖 Springockito,因为它在这个阶段是不可靠的。

但是这些都不起作用,因为用例是所有这些的组合。


可能的解决方案

  • 删除@Autowired字段并使用Setter 注入,以便可以通过使用 PowerMock(已测试工作)正常模拟来实现 - 但这是外部团队包遵循的约定 - 我应该尽我所能坚持下去。
  • 或将@Autowired 设置为设置器或构造器

备择方案?

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

  • 任何其他不需要密切关注被测类的方法——如果我没有修改这个类的权限怎么办?即,纯测试库依赖解决方案。
  • 不确定PowerMockito是否可行?没有尝试过PowerMockito 与 PowerMock的组合。
4

1 回答 1

1

唔,

不确定 PowerMockito 是否可行?还没有尝试过 PowerMockito 与 PowerMock 的组合。

在我看来,你脑子里一团糟,误解了 PowerMock/Mockito 和 EasyMock。

永远不要同时使用PowerMockitoand PowerMock,因为这两个类是 PowerMock 友好的 API,适用于两个不同的模拟框架:EasyMockMockito。并且没有理由同时使用它们。

当然,这需要工作

//@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());

    }

}
于 2016-04-23T18:38:31.820 回答