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

news/2024/11/14 3:29:40

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


http://www.niftyadmin.cn/n/4057554.html

相关文章

俺是郭德纲先生的忠实非现场 听众+观众

俺从小喜欢听相声&#xff0c;在郭先生火爆之前就常年到处下载&#xff0c;曾经将某网站所有的相声都下载下来过。在俺看来&#xff0c;郭德纲先生的相声说的非常好&#xff0c;能够将俺逗乐&#xff0c;这说明郭德纲先生干一行爱一行、说相声首先就是搞笑的。 在郭先生被jb台陷…

回了一趟老家

周末回了一趟老家&#xff0c;又节能减排限电了&#xff0c;家里有两个鸵鸟蛋&#xff0c;可惜忘了照照片了&#xff0c;也忘了带过来了&#xff0c;家里养的几盆菊花开的很盛。 听说家里买了几根竹杆&#xff0c;到地里锯了几个竹根过来&#xff0c;不是很好&#xff0c;但是经…

Java单元测试实践-19.Mockito与PowerMock的其他功能

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Mockito与PowerMock的其他功能 1.1. 获取Mock对象详细信息 Mockito提供了用于获取Mock对象详细信息的API&#xff0c;可参考 htt…

Java单元测试实践-01.单元测试概述与示例

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. 前言 以下内容为本人以开发人员的视角&#xff0c;在平时进行单元测试过程中的总结。主要内容为通用的&#xff0c;不限制具体业务…

Java单元测试实践-04.使用IDEA、Eclipse执行单元测试

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. 使用IDEA、Eclipse执行单元测试 使用IDE执行单元测试是常见的执行方式&#xff0c;以下针对IntelliJ IDEA与Eclipse进行说明。 1…

Java单元测试实践-08.Stub、Replace、Suppress静态方法

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Stub、Replace、Suppress静态方法 以下主要说明&#xff0c;如何使用PowerMockito对静态方法进行Stub、Replace&#xff0c;以及对…

Java单元测试实践-14.Mock、Spy后Stub Spring成员变量中的方法

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Mock/Spy后Stub Spring成员变量中的方法 在被测试代码中&#xff0c;Spring的Component组件实现类中通常会依赖其他的成员变量&am…

Java单元测试实践-22.Gradle资源文件与配置参数动态替换

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Gradle资源文件与配置参数动态替换 1.1. main模块与test模块资源文件 Gradle任务processResources、processTestResources分别用…