单元测试工具-PowerMock

邮差的信 提交于 2020-07-25 23:59:55

单元测试工具-PowerMock

    Mock:为某个类创建一个虚假的对象,在测试时用这个虚假的对象替换掉真实的对象。

    PowerMock实现原理:
        1)PowerMock会创建一个org.powermock.core.classloader.MockClassLoader类加载器,然后该类加载器会去加载测试用例使用到的类(系统类除外)。
        2)PowerMock会去修改写在注解@PrepareForTest里的class文件(注:当前测试类会自动加入该注解中),以实现mock的功能。
        3)如果需要mock系统类的final方法和静态方法,PowerMock不会直接修改系统类的class文件,而是修改调用系统类的class文件。

    UT指标:
        行覆盖、函数覆盖、分支覆盖
        增量行覆盖、增量函数覆盖、增量分支覆盖
        注意:AClassTest和AClass的包路径必须一致,否则单测覆盖率统计不到。 

    实现:
        第一步:
            maven依赖:
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.8</version>
                    <scope>test</scope>
                </dependency>


                <dependency>
                    <groupId>org.powermock</groupId>
                    <artifactId>powermock-module-junit4</artifactId>
                    <version>1.7.0</version>
                    <scope>test</scope>
                </dependency>
                <dependency>
                    <groupId>org.powermock</groupId>
                    <artifactId>powermock-api-mockito</artifactId>
                    <version>1.7.0</version>
                    <scope>test</scope>
                </dependency>

        第二步:
            添加注解:
                @RunWith(PowerMockRunner.class)
                @PrepareForTest({ClassA.class})
                public class UserServiceImplTest {

                    @InjectMocks
                    private ClassA clazzA; 

                    @Mock
                    private ClassB clazzB;  

                    @Mock
                    private ClassC clazzC);

                    // 实例化@InjectMocks注解修饰的属性!
                    @Before
                    public void setUp() {
                        MockitoAnnotations.initMocks(this);
                    }


                    @Test
                    public void testMethodA() {

                        // Mock一般方法:
                        List myList = new ArrayList();
                        PowerMockito.when(clazzA.methodA(Mockito.anyInt(), Mockito.anyString(), Mockito.anyObject()).thenReturn(myList);
                        List result = clazzA.methodA(17, "test", new ArrayList());
                        Assert.assertEquals(myList, result);


                    }
                }

                说明:
                    @RunWith(PowerMockRunner.class)注解:表示使用PowerMockerRunner来运行测试用例,否则无法使用powermock。

                    @PrepareForTest({xx.class, xxx.class})注解:声明需要测试的类(即:要测试的目标方法所在的类 + 目标方法中使用到的且需要mock的其它类),以逗号分隔。
                    注意:
                        1>普通Mock(mock一般方法)不需要加@RunWith(PowerMockRunner.class)和@PrepareForTest({xx.class, xxx.class})注解!
                        2>非普通mock(mock静态方法、私有方法、构造方法、final方法、抽象方法)必须加@RunWith(PowerMockRunner.class)和@PrepareForTest({xx.class, xxx.class})注解!

                    @InjectMocks注解:Mockito会将@Mock或@Spy修饰的属性自动注入到@InjectMocks修饰的属性中。

                    @Mock注解:为某个类创建一个虚假的对象,在测试时用这个虚假的对象替换掉真实的对象。

                    @Spy注解:当一个对象中的部分方法需要mock,部分方法需要真实调用的时候,我们可以使用该注解来解决这个问题。

                    要想使以上注解生效,则必须在执行测试方法前 先实例化@InjectMocks注解修饰的属性:MockitoAnnotations.initMocks(this);


                Mock方法: 

                    注意:如果方法的参数不是mock出来的,则调用方法的实例就不会是mock出来的了!!!

                    Mock一般方法:
                        PowerMockito.when(clazzA.methodA(Mockito.anyObject())).thenReturn("ok");
                        PowerMockito.doThrow(new XxxException("")).when(clazzA).methodA(Mockito.any(XXX.class))
                        注意:doThrow中抛出异常的范围不能超过methodA方法抛出的异常的范围。若methodA没有显示声明抛出异常,则我们可以thenThrow(new RuntimeException(""))。

                    Mock静态方法:
                        PowerMockito.mockStatic(MyUtils.class);
                        PowerMockito.when(MyUtils.methodA()).thenReturn("ok");
                        注:若MyUtils的方法为static void ,则默认会mock掉。

                    Mock私有方法:
                        PowerMockito.when(clazzA, "methodB").thenReturn("ok");
                        PowerMockito.when(clazzA, "methodB", param1, param2).thenReturn("ok");
                        PowerMockito.when(clazzA, "methodB").thenCallRealMethod();

                        举例:PowerMockito.when(clazzA, "methodB", Mockito.anyObject(), Mockito.anyObject()).thenReturn("ok");

                        说明:也可以考虑使用反射来修改类的私有属性、调用类的私有方法。
                        举例:
                            ReflectionTestUtils.setField(targetObject, "token", "123");
                            ReflectionTestUtils.invokeMethod(targetObject, "methodName", param1, param2);


                    Mock构造方法:
                        说明:当我们需要mock方法内new出来的变量时,我们可以通过mock构造方法来实现。
                        // mock file对象
                        PowerMockito.whenNew(File.class).withArguments(Mockito.anyString()).thenReturn(new File("aaa")); 

                        // mock file对象并且打桩
                        File mockFile = Mockito.mock(File.class);
                        PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(mockFile);
                        PowerMockito.when(mockFile.canExecute()).thenReturn(false);
                        // PowerMockito.when(mockFile.canExecute()).thenThrow(new RuntimeException());


                    Mock抽象方法:
                        说明:我们一般选择去mock抽象类的实现类,而不是直接mock抽象类。


                断言的场景:
                    断言方法的返回值与期望的返回值相同:        Assert.assertEquals(result, expectResult);
                    断言方法被调用n次
                        一般用来判断void方法:    Mockito.verify(aaaService, Mockito.times(n)).method1(Mockito.anyString(), Mockito.any(Xxx.class)); 

                        使用PowerMockito检查某个静态方法调用的次数:
                            步骤:先调用对应的静态方法,再启用静态检查,最后再调用对应的静态方法。这样的用法比较奇怪,记住即可。
                            举例:验证Runtime.getRuntime()是否被调用了2次。

                                PowerMockito.mockStatic(Runtime.class);

                                Runtime.getRuntime();
                                Runtime.getRuntime();
                                // do other things

                                PowerMockito.verifyStatic(Mockito.times(2));
                                Runtime.getRuntime();

                    
                    
    常见的错误:
        org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'.
        解决:when()方法中的参数要求是一个mock对象的方法调用。报该错误的原因是调用方法的对象不是mock出来的对象。如果是调用静态方法,则使用PowerMockito.mockStatic(类名.class);


        org.powermock.api.mockito.ClassNotPreparedException: The class XXX not prepared for test.
        解决:将XXX.class添加到@PrepareForTest中。


        
 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!