我想对带有自动装配的最终类对象的 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解决
- EasyMock和PowerMock没有@InjectMocks注释来通过 Mockito 尽可能地注入模拟(将在几秒钟内解决问题)。
- 可以通过SpringJUnit4Runner和单独的单元测试@ContextConfiguration注入自动装配的 spring bean
- 可以使用PowerMockRunnerDelegate使用PowerMockRunner和SpringJUnit4Runner运行相同的测试文件
- 我知道@PostConstruct方法如果在代码中模拟而不是通过使用spring bean 创建和注入将不会自动执行。
- 如果编写了包装器工厂 bean 类并用于创建模拟,它会自动注入,但同时调用 @PostConstruct 方法。
- 不可能依赖 Springockito,因为它在这个阶段是不可靠的。
但是这些都不起作用,因为用例是所有这些的组合。
可能的解决方案
- 删除@Autowired字段并使用Setter 注入,以便可以通过使用 PowerMock(已测试工作)正常模拟来实现 - 但这是外部团队包遵循的约定 - 我应该尽我所能坚持下去。
- 或将@Autowired 设置为设置器或构造器
备择方案?
我不认为这些类需要重组,因为它们服务于它们的目的并且设计得很好。
- 任何其他不需要密切关注被测类的方法——如果我没有修改这个类的权限怎么办?即,纯测试库依赖解决方案。
- 不确定PowerMockito是否可行?没有尝试过PowerMockito 与 PowerMock的组合。