Java单元测试实践-18.使用注解进行Stub、Replace、Suppress

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类。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值