Java单元测试实践-00.目录(9万多字文档+700多测试示例)
https://blog.csdn.net/a82514921/article/details/107969340
1. 使用@MockPolicy注解进行Stub、Replace、Suppress
@MockPolicy注解的说明可参考 https://javadoc.io/doc/org.powermock/powermock-core/latest/org/powermock/core/classloader/annotations/MockPolicy.html 。
使用@MockPolicy注解可以实现例如禁止指定的方法,禁止静态初始化方法或拦截方法调用,并修改返回值等功能。可以避免为测试编写重复的设置代码。
使用@MockPolicy注解时,需要指定在测试类中使用的PowerMockPolicy接口实现类,例如“@MockPolicy(TestPolicyStub1.class)”。
1.1. PowerMockPolicy使用方法
PowerMockPolicy接口的说明可参考 https://javadoc.io/doc/org.powermock/powermock-core/latest/org/powermock/core/spi/PowerMockPolicy.html 。
PowerMockPolicy接口包含了两个方法applyClassLoadingPolicy()与applyInterceptionPolicy(),PowerMock需要知道在加载这些类之前,Mock类加载器应当修改哪些类。applyClassLoadingPolicy()方法告诉PowerMock应该加载哪些类,之后Mock类加载器会调用applyInterceptionPolicy()方法。
applyClassLoadingPolicy()方法,用于实施在拦截策略发生之前,必须完成的类加载相关的全部策略。
applyInterceptionPolicy()方法,用于实施拦截策略。
在PowerMockPolicy接口的实现类中,需要在applyClassLoadingPolicy()方法中指定需要被PowerMock进行准备的类(与@PrepareForTest注解类似),需要在applyInterceptionPolicy()方法中指定对于指定类的Stub、Replace或Suppress等处理。
1.2. 使用@MockPolicy注解进行Stub操作
在@MockPolicy注解指定的PowerMockPolicy实现类的applyClassLoadingPolicy()方法中,若不对需要进行Stub操作的类进行处理,则Stub操作不会生效。示例如下,可参考示例TestPolicyStub1(TestMockPolicyStub1)类。
// PowerMockPolicy实现类
public class TestPolicyStub1 implements PowerMockPolicy {
@Override
public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
}
@Override
public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
settings.stubMethod(PowerMockito.method(TestStaticPublicNonVoid1.class, TestStaticPublicNonVoid1.NAME_TEST1),
TestConstants.MOCKED);
}
}
// 测试类
@MockPolicy(TestPolicyStub1.class)
在@MockPolicy注解指定的PowerMockPolicy实现类的applyClassLoadingPolicy()方法中,需要对Stub操作对应的类进行处理,可以调用MockPolicyClassLoadingSettings settings参数的addFullyQualifiedNamesOfClassesToLoadByMockClassloader()方法,指定Stub操作对应的类名。addFullyQualifiedNamesOfClassesToLoadByMockClassloader()方法支持传入一个或多个类名,或类名数组。示例如下,可参考示例TestPolicyStub2(TestMockPolicyStub2)、TestPolicyStub3(TestMockPolicyStub3)、TestPolicyStub5(TestMockPolicyStub5)类。
// PowerMockPolicy实现类
public class TestPolicyStub2 implements PowerMockPolicy {
@Override
public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestStaticPublicNonVoid1.class.getName());
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestPublicNonVoidService1Impl.class.getName());
}
@Override
public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
settings.stubMethod(PowerMockito.method(TestStaticPublicNonVoid1.class, TestStaticPublicNonVoid1.NAME_TEST1),
TestConstants.MOCKED);
settings.stubMethod(PowerMockito.method(TestPublicNonVoidService1Impl.class, TestPublicNonVoidService1Impl
.NAME_TEST1), TestConstants.MOCKED);
}
}
// 测试类
@MockPolicy(TestPolicyStub2.class)
在PowerMockPolicy实现类的applyInterceptionPolicy()方法中执行Stub操作时,可以使用MockPolicyInterceptionSettings类的stubMethod()方法,添加方法及对应的Stub操作的返回值。也可以使用setMethodsToStub()方法,指定需要进行Stub的方法,及对应的返回值组成的Map,该方法会将之前的配置覆盖。示例如下,可参考示例TestPolicyStub2(TestMockPolicyStub2)、TestPolicyStub3(TestMockPolicyStub3)类。
// PowerMockPolicy实现类
public class TestPolicyStub3 implements PowerMockPolicy {
@Override
public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(
new String[]{TestStaticPublicNonVoid1.class.getName(), TestPublicNonVoidService1Impl.class.getName()});
}
@Override
public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
Map<Method, Object> map = new HashMap<>(2);
map.put(PowerMockito.method(TestStaticPublicNonVoid1.class, TestStaticPublicNonVoid1.NAME_TEST1),
TestConstants.MOCKED);
map.put(PowerMockito.method(TestPublicNonVoidService1Impl.class, TestPublicNonVoidService1Impl.NAME_TEST1),
TestConstants.MOCKED);
settings.setMethodsToStub(map);
}
}
// 测试类
@MockPolicy(TestPolicyStub3.class)
MockPolicyClassLoadingSettings类包含setFullyQualifiedNamesOfClassesToLoadByMockClassloader()方法,该方法用于设置Mock类加载器需要加载的类名,在执行时会将之前的配置覆盖,使用时需要注意。
@MockPolicy注解支持继承,子类会继承超类中指定的@MockPolicy注解。可参考示例TestMockPolicyStub2Child类。
@MockPolicy注解支持以数组形式指定多个PowerMockPolicy实现类。如“@MockPolicy({TestPolicyStub4Multi1.class, TestPolicyStub4Multi2.class})”,可参考示例TestMockPolicyStub4A类。
在@BeforeClass对应的方法中,PowerMockPolicy实现类中的Stub操作还未生效;在@Before、@Test对应的方法中,PowerMockPolicy实现类中的Stub操作已生效。可参考示例TestMockPolicyStub4B类。
在PowerMockPolicy实现类的applyClassLoadingPolicy()方法中,指定需要处理的类,与@PrepareForTest注解的效果类似。可参考示例TestMockPolicyStub5、TestPolicyStub5类。
1.3. 使用@MockPolicy注解进行Replace操作
在PowerMockPolicy实现类的applyInterceptionPolicy()方法中,可以使用MockPolicyInterceptionSettings settings参数的proxyMethod()方法添加方法及对应的Replace操作的InvocationHandler实例,可以修改返回值、抛出异常或执行真实方法。示例如下,可参考示例TestPolicyReplace1(TestMockPolicyReplace1)、TestPolicyReplace2(TestMockPolicyReplace2)、TestPolicyReplace3(TestMockPolicyReplace3)类。
// PowerMockPolicy实现类
public class TestPolicyReplace1 implements PowerMockPolicy {
@Override
public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestStaticPublicNonVoid1.class.getName());
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestPublicNonVoidService1Impl.class.getName());
}
@Override
public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
settings.proxyMethod(PowerMockito.method(TestStaticPublicNonVoid1.class, TestStaticPublicNonVoid1.NAME_TEST1)
, (proxy, method, args) -> TestConstants.MOCKED);
settings.proxyMethod(PowerMockito.method(TestPublicNonVoidService1Impl.class, TestPublicNonVoidService1Impl
.NAME_TEST1), (proxy, method, args) -> TestConstants.MOCKED);
}
}
// 测试类
@MockPolicy(TestPolicyReplace1.class)
在PowerMockPolicy实现类的applyInterceptionPolicy()方法中,也可以使用setMethodsToProxy()方法,指定需要进行Replace的方法,及对应的返回值组成的Map,该方法会将之前的配置覆盖。示例如下,可参考示例TestPolicyReplace4(TestMockPolicyReplace4)类。
// PowerMockPolicy实现类
public class TestPolicyReplace4 implements PowerMockPolicy {
@Override
public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(
new String[]{TestStaticPublicNonVoid1.class.getName(), TestPublicNonVoidService1Impl.class.getName()});
}
@Override
public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
Map<Method, InvocationHandler> map = new HashMap<>(2);
map.put(PowerMockito.method(TestStaticPublicNonVoid1.class, TestStaticPublicNonVoid1.NAME_TEST1),
(proxy, method, args) -> TestConstants.MOCKED);
map.put(PowerMockito.method(TestPublicNonVoidService1Impl.class, TestPublicNonVoidService1Impl.NAME_TEST1),
(proxy, method, args) -> TestConstants.MOCKED);
settings.setMethodsToProxy(map);
}
}
// 测试类
@MockPolicy(TestPolicyReplace4.class)
1.4. 使用@MockPolicy注解进行Suppress操作
在PowerMockPolicy实现类的applyInterceptionPolicy()方法中,可以使用MockPolicyInterceptionSettings settings参数的addFieldToSuppress()方法添加需要Suppress的字段,该方法支持传入一个或多个Field对象。示例如下,可参考示例TestPolicySuppress1(TestMockPolicySuppress1)、TestPolicySuppress2(TestMockPolicySuppress2)类。
// PowerMockPolicy实现类
public class TestPolicySuppress1 implements PowerMockPolicy {
@Override
public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestStaticPublicNonVoid1.class.getName());
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestServiceB1Impl.class.getName());
}
@Override
public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
settings.addFieldToSuppress(
PowerMockito.field(TestStaticPublicNonVoid1.class, "flag"),
PowerMockito.field(TestServiceB1Impl.class, "testServiceA1"));
}
}
// 测试类
@MockPolicy(TestPolicySuppress1.class)
在PowerMockPolicy实现类的applyInterceptionPolicy()方法中,也可以使用MockPolicyInterceptionSettings settings参数的addMethodsToSuppress()方法添加需要Suppress的方法,该方法支持传入一个或多个Method对象。示例如下,可参考示例TestPolicySuppress3(TestMockPolicySuppress3)、TestPolicySuppress4(TestMockPolicySuppress4)类。
// PowerMockPolicy实现类
public class TestPolicySuppress3 implements PowerMockPolicy {
@Override
public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestStaticPublicNonVoid1.class.getName());
settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(TestServiceB1Impl.class.getName());
}
@Override
public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
settings.addMethodsToSuppress(
PowerMockito.method(TestStaticPublicNonVoid1.class, TestStaticPublicNonVoid1.NAME_TEST1),
PowerMockito.method(TestServiceB1Impl.class, TestServiceB1Impl.NAME_TEST1));
}
}
// 测试类
@MockPolicy(TestPolicySuppress3.class)
1.5. 使用多个PowerMockPolicy实现类时的生效情况
在@MockPolicy注解中可以指定多个PowerMockPolicy实现类。
当在不同的PowerMockPolicy实现类中对同一个方法进行Stub操作时,最后指定的PowerMockPolicy实现类中对该方法的Stub操作生效;在PowerMockPolicy实现类中执行Replace操作时也是如此。
例如使用@MockPolicy注解分别指定了PowerMockPolicy实现类TestPolicyOrderReplaceA、TestPolicyOrderReplaceB,“@MockPolicy({TestPolicyOrderReplaceA.class, TestPolicyOrderReplaceB.class})”,在TestPolicyOrderReplaceA、TestPolicyOrderReplaceB类中对同一个方法进行了Replace,由于TestPolicyOrderReplaceB类在@MockPolicy注解中的顺序靠后,因此TestPolicyOrderReplaceB类的Replace操作生效,TestPolicyOrderReplaceA类的Replace操作不生效。
可参考示例TestMockPolicyOrderReplace1、TestMockPolicyOrderReplace2、TestMockPolicyOrderStub1、TestMockPolicyOrderStub2类。
在@MockPolicy注解中指定多个PowerMockPolicy实现类对同一个方法进行处理,分别进行Stub、Replace操作时,Stub操作生效,Replace操作不生效,无论以上PowerMockPolicy实现类的顺序如何指定。
例如使用@MockPolicy注解分别指定了PowerMockPolicy实现类TestPolicyOrderReplaceA、TestPolicyOrderStubB,“@MockPolicy({TestPolicyOrderReplaceA.class, TestPolicyOrderStubB.class, TestPolicyOrderReplaceA.class})”,在TestPolicyOrderReplaceA、TestPolicyOrderStubB类中对同一个方法分别进行了Replace、Stub操作,TestPolicyOrderStubB类中的Stub操作生效,TestPolicyOrderReplaceA类中的Replace操作不生效。
可参考示例TestMockPolicyOrderSR1、TestMockPolicyOrderRS1类。
1.6. @MockPolicy注解与Stub、Replace、Suppress同时使用的生效情况
通过@MockPolicy注解指定的PowerMockPolicy实现类进行Stub、Replace、Suppress操作,与使用PowerMock.stub()/replace()/suppress()方法效果相同。当@MockPolicy注解与Stub、Replace、Suppress操作同时处理同一个方法时,可参考前文“对同一个方法执行Mock后Stub、Stub、Replace、Suppress等操作的生效情况”部分。可参考示例adrninistrator.test.testmock.mockpolicy.mix包中的类。
1.7. @PrepareForTest注解导致@MockPolicy注解失效
在使用了@MockPolicy注解的测试类中,若在类级别使用了@PrepareForTest注解,无论是否指定类,都会导致测试类的所有@Test方法中@MockPolicy注解失效。可参考示例TestMockPolicyConflictStC1、TestMockPolicyConflictStC2类。
在使用了@MockPolicy注解的测试类中,若在@Test方法级别使用了@PrepareForTest注解,无论是否指定类,都会导致当前@Test方法中@MockPolicy注解失效。可参考示例TestMockPolicyConflictStM1、TestMockPolicyConflictStM2类。
@PrepareForTest注解支持继承,子类会继承超类中指定的@PrepareForTest注解,会导致@MockPolicy注解失效。可参考示例TestMockPolicyConflictStCChild类。
以上示例中的@MockPolicy注解指定了Stub操作,对于通过@MockPolicy注解指定Replace、Suppress操作时,也会因@PrepareForTest注解而失效,可参考示例TestMockPolicyConflictReC、TestMockPolicyConflictReM、TestMockPolicyConflictSuC、TestMockPolicyConflictSuM类。
1.7.1. 使用自定义注解替代@MockPolicy注解的部分功能
在使用PowerMock时,经常需要使用@PrepareForTest注解,例如需要对静态、私有等方法进行Mock,@PrepareForTest注解导致@MockPolicy注解失效的问题,使得@MockPolicy注解的使用场景受限。
可以使用自定义注解,达到与@MockPolicy注解类似的功能,即通过注解进行Stub、Replace、Suppress等操作,有利于代码复用,减少重复代码,且不会因@PrepareForTest注解导致失效。
在示例代码中,@TestInitAnnotation为与@MockPolicy功能类似的自定义注解,在其中需要指定TestInitInterface实现类数组。
TestInitInterface接口与PowerMockPolicy类似,TestInitInterface接口的init()方法用于在@Test方法执行前执行初始化操作,可进行Stub、Replace、Suppress等操作。
在测试基类TestMockBase中通过@TestExecutionListeners指定的TestExecutionListener实现类TestCommonExecutionListener的beforeTestMethod方法中,会获取测试类的@TestInitAnnotation注解指定的TestInitInterface实现类,依次执行其init()方法。当需要使用上述自定义注解时,测试类应继承TestMockBase类,并使用@PrepareForTest注解指定被Stub、Replace或Suppress的类名。示例如下:
// TestInitInterface实现类
public class TestInitReplace implements TestInitInterface {
@Override
public void init() {
PowerMockito.replace(PowerMockito.method(TestStaticPublicNonVoid1.class, TestStaticPublicNonVoid1.NAME_TEST1)).with(
(proxy, method, args) -> TestConstants.MOCKED);
PowerMockito.replace(PowerMockito.method(TestPublicNonVoidService1Impl.class, TestPublicNonVoidService1Impl.NAME_TEST1)).with(
(proxy, method, args) -> TestConstants.MOCKED);
}
}
// 测试类
@TestInitAnnotation({TestInitReplace.class})
@PrepareForTest({TestStaticPublicNonVoid1.class, TestPublicNonVoidService1Impl.class})
可参考示例TestInitMethodReplace、TestInitMethodStub、TestInitMethodSuppress类。