当前位置: 代码迷 >> Java相关 >> 使用PowerMock开展Mock测试
  详细解决方案

使用PowerMock开展Mock测试

热度:102   发布时间:2016-04-22 19:30:41.0
使用PowerMock进行Mock测试

安装

下载地址:https://github.com/jayway/powermock/wiki/Downloads。下载" Mockito and JUnit including dependencies"版本。当前版本为”powermock-mockito-junit-1.6.3.zip"。

IntelliJ IDEA的设置如下:

右击工程,选择“Open Module Settings”

按下“ALT + Insert”,选择“Jars or directories...", 插入jar包:

点击OK。

在”Module Settings”对话框中点击“Sources”标签,右击右边底部面板,选择“New Folder...", 命名为test。

在”Module Settings”对话框中选择test,标识为Test Sources,关闭”Module Settings”对话框

Eclipse中只需要上述jar包放在工程下的lib目录即可。

Maven在pom.xml添加如下内容:

快速入门

下面创建EmployeeController类用于给Employee类执行Create, Read, Update, and Delete (CRUD)。实际工作由EmployeeService完成。getProjectedEmployeeCount方法预计公司员工每年增加20%,并返回近似取整。

public class EmployeeController {        private EmployeeService employeeService;        public EmployeeController(EmployeeService employeeService) {        this.employeeService = employeeService;    }        public int getProjectedEmployeeCount() {        final int actualEmployeeCount = employeeService.getEmployeeCount();        return (int) Math.ceil(actualEmployeeCount * 1.2);    }        public void saveEmployee(Employee employee) {        employeeService.save(employee);    }    }

 

public class EmployeeService {        public int getEmployeeCount() {        throw new UnsupportedOperationException();    }        public void save(Employee employee) {        throw new UnsupportedOperationException();    }    }

 

由于getEmployeeCount等方法没有真正实现,我们需要mock:

public class Employee {}
import static org.junit.Assert.*;import org.junit.Test;import org.mockito.Mockito;import org.powermock.api.mockito.PowerMockito;public class EmployeeControllerTest {    @Test    public void shouldReturnProjectedCountOfEmployeesFromTheService() {                EmployeeService mock = PowerMockito.mock(EmployeeService.class);        PowerMockito.when(mock.getEmployeeCount()).thenReturn(8);        EmployeeController employeeController = new EmployeeController(mock);        assertEquals(10, employeeController.getProjectedEmployeeCount());    }        @Test    public void    shouldInvokeSaveEmployeeOnTheServiceWhileSavingTheEmployee() {                EmployeeService mock = PowerMockito.mock(EmployeeService.class);            EmployeeController employeeController = new EmployeeController(mock);        Employee employee = new Employee();        employeeController.saveEmployee(employee);        Mockito.verify(mock).saveEmployee(employee);    }        }

上面的saveEmployee(Employee)没有返回值,我们只需要用verify确认有调用即可。

另外有个非常用的MockSettings功能,用于设置mock名、实现额外接口(参见https://groups.google.com/forum/?fromgroups=#!topic/mockito/YM5EF0x90_4)、开启详细日志、注册listener用于mock时通知消息调用。比如:

        EmployeeService mock = PowerMockito.mock(EmployeeService.class, Mockito.withSettings()                .name("EmployeeServiceMock").verboseLogging());

 

模拟静态方法

修改类Employee:

public class Employee {        public static int count() {        throw new UnsupportedOperationException();        }}

新建EmployeeServiceTest类:

import static org.junit.Assert.*;import org.junit.Test;import org.junit.runner.RunWith;import org.powermock.api.mockito.PowerMockito;import org.powermock.core.classloader.annotations.PrepareForTest;import org.powermock.modules.junit4.PowerMockRunner;@RunWith(PowerMockRunner.class)@PrepareForTest(Employee.class)public class EmployeeServiceTest {    @Test    public void shouldReturnTheCountOfEmployeesUsingTheDomainClass() {                    PowerMockito.mockStatic(Employee.class);            PowerMockito.when(Employee.count()).thenReturn(900);                    EmployeeService employeeService = new            EmployeeService();            assertEquals(900, employeeService.getEmployeeCount());            }}

@RunWith(PowerMockRunner.class)语句告诉JUnit用PowerMockRunner执行测试。
@PrepareForTest(Employee.class)语句告诉PowerMock准备Employee类进行测试。适用于模拟final类或有final, private, static, native
方法的类。
注意这里使用的是mockStatic而不是上面的mock。

下面我们模拟下返回void的静态方法。在EmployeeService添加加薪方法:

public class EmployeeService {            public int getEmployeeCount() {                throw new UnsupportedOperationException();        }            public void save(Employee employee) {                throw new UnsupportedOperationException();        }                public boolean giveIncrementToAllEmployeesOf(int percentage) {                try{                        Employee.giveIncrementOf(percentage);                        return true;                } catch(Exception e) {                        return false;                }        }    }

修改EmployeeServiceTest类

import static org.junit.Assert.*;import org.junit.Test;import org.junit.runner.RunWith;import org.powermock.api.mockito.PowerMockito;import org.powermock.core.classloader.annotations.PrepareForTest;import org.powermock.modules.junit4.PowerMockRunner;@RunWith(PowerMockRunner.class)@PrepareForTest(Employee.class)public class EmployeeServiceTest {        @Test        public void shouldReturnTrueWhenIncrementOf10PercentageIsGivenSuccessfully() {                            PowerMockito.mockStatic(Employee.class);                PowerMockito.doNothing().when(Employee.class);                Employee.giveIncrementOf(10);                EmployeeService employeeService = new EmployeeService();                assertTrue(employeeService.giveIncrementToAllEmployeesOf(10));            }                    @Test            public void shouldReturnFalseWhenIncrementOf10PercentageIsNotGivenSuccessfully() {                    PowerMockito.mockStatic(Employee.class);                    PowerMockito.doThrow(new IllegalStateException()).when(Employee.class);                    Employee.giveIncrementOf(10);                    EmployeeService employeeService = new EmployeeService();                    assertFalse(employeeService.giveIncrementToAllEmployeesOf(10));            }}

PowerMockito.doNothing方法告诉PowerMock下一个方法调用时什么也不做。
PowerMockito.doThrow方法告诉PowerMock下一个方法调用时产生异常。

PowerMock使用自定义类加载器和字节码操作来模拟静态方法。对于实例中没有mock的方法,也有默认返回值,比如返回int类型的方法,默认返回0。
PowerMockito.doNothing和PowerMockito.doThrow的语法可用于实例方法

先修改Employee类:

public class Employee {            public static int count() {                throw new UnsupportedOperationException();        }                public static void giveIncrementOf(int percentage) {                throw new UnsupportedOperationException();        }                public void save() {                throw new UnsupportedOperationException();        }            }

创建测试类:

import static org.junit.Assert.*;import org.junit.Test;import org.powermock.api.mockito.PowerMockito;public class EmployeeTest {        @Test()    public void shouldNotDoAnythingIfEmployeeWasSaved() {        Employee employee = PowerMockito.mock(Employee.class);        PowerMockito.doNothing().when(employee).save();        try {            employee.save();        } catch(Exception e) {            fail("Should not have thrown an exception");        }    }    @Test(expected = IllegalStateException.class)    public void shouldThrowAnExceptionIfEmployeeWasNotSaved() {        Employee employee =        PowerMockito.mock(Employee.class);        PowerMockito.doThrow(new IllegalStateException()).when(employee).save();        employee.save();    }}

注意这里doThrow和doNothing方法不会对下一行产生影响。

验证方法调用

验证断言方法是否调用。

修改EmployeeService类的saveEmployee方法。

 

public class EmployeeService {            public int getEmployeeCount() {                throw new UnsupportedOperationException();        }            public void saveEmployee(Employee employee) {            if(employee.isNew()) {                employee.create();                return;            }            employee.update();        }            public boolean giveIncrementToAllEmployeesOf(int percentage) {                try{                        Employee.giveIncrementOf(percentage);                        return true;                } catch(Exception e) {                        return false;                }        }    }

 

修改Employee类:

public class Employee {            public static int count() {                throw new UnsupportedOperationException();        }                public static void giveIncrementOf(int percentage) {                throw new UnsupportedOperationException();        }                public boolean isNew() {            throw new UnsupportedOperationException();        }                public void update() {            throw new UnsupportedOperationException();        }                public void create() {            throw new UnsupportedOperationException();        }                public void save() {                throw new UnsupportedOperationException();        }            }

在EmployeeServiceTest类中新增shouldCreateNewEmployeeIfEmployeeIsNew方法:

import static org.junit.Assert.*;import org.junit.Test;import org.junit.runner.RunWith;import org.mockito.Mockito;import org.powermock.api.mockito.PowerMockito;import org.powermock.core.classloader.annotations.PrepareForTest;import org.powermock.modules.junit4.PowerMockRunner;@RunWith(PowerMockRunner.class)@PrepareForTest(Employee.class)public class EmployeeServiceTest {        @Test        public void shouldReturnTrueWhenIncrementOf10PercentageIsGivenSuccessfully() {                            PowerMockito.mockStatic(Employee.class);                PowerMockito.doNothing().when(Employee.class);                Employee.giveIncrementOf(10);                EmployeeService employeeService = new EmployeeService();                assertTrue(employeeService.giveIncrementToAllEmployeesOf(10));            }                    @Test            public void shouldReturnFalseWhenIncrementOf10PercentageIsNotGivenSuccessfully() {                    PowerMockito.mockStatic(Employee.class);                    PowerMockito.doThrow(new                    IllegalStateException()).when(Employee.class);                    Employee.giveIncrementOf(10);                    EmployeeService employeeService = new EmployeeService();                    assertFalse(employeeService.giveIncrementToAllEmployeesOf(10));            }                        @Test            public void shouldCreateNewEmployeeIfEmployeeIsNew() {                    Employee mock = PowerMockito.mock(Employee.class);                    PowerMockito.when(mock.isNew()).thenReturn(true);                    EmployeeService employeeService = new EmployeeService();                    employeeService.saveEmployee(mock);                    Mockito.verify(mock).create();                    Mockito.verify(mock, Mockito.never()).update();            }            }

 

 Mockito.verify(mock).create()验证调用了create方法。 Mockito.verify(mock, Mockito.never()).update();验证没有调用update方法。

下面验证静态方法,在EmployeeServiceTest类添加shouldInvoke_giveIncrementOfMethodOnEmployeeWhileGivingIncrement方法:

            @Test            public void shouldInvoke_giveIncrementOfMethodOnEmployeeWhileGivingIncrement() {                    PowerMockito.mockStatic(Employee.class);                    PowerMockito.doNothing().when(Employee.class);                    Employee.giveIncrementOf(9);                    EmployeeService employeeService = new EmployeeService();                    employeeService.giveIncrementToAllEmployeesOf(9);                    PowerMockito.verifyStatic();                    Employee.giveIncrementOf(9);            }

同样,静态验证也要分两步走。

其他验证模式可以验证调用次数:

  • Mockito.times(int n) : This verification mode asserts that the mocked method was invoked exactly 'n' times
  • Mockito.atLeastOnce() : This verification mode asserts that the mocked method was invoked at least once
  • Mockito.atLeast(int n) : This verification mode asserts that the mocked method was invoked at least 'n' times
  • Mockito.atMost(int n) : This verification mode asserts that the mocked method was invoked at most 'n' times

使用Mockito.inOrder还可以验证调用的顺序:

            @Test            public void shouldInvokeIsNewBeforeInvokingCreate() {                                Employee mock = PowerMockito.mock(Employee.class);                PowerMockito.when(mock.isNew()).thenReturn(true);                EmployeeService employeeService = new EmployeeService();                employeeService.saveEmployee(mock);                InOrder inOrder = Mockito.inOrder(mock);                inOrder.verify(mock).isNew();                Mockito.verify(mock).create();                Mockito.verify(mock, Mockito.never()).update();            }