为什么使用PowerMock
PowerMock
PowerMock支持JUnit和TestNG,扩展了EasyMock和Mockito框架,增加了mock static、final方法的功能。本文介绍基于JUnit框架的PowerMock。
1. PowerMock注解@RunWith
与@PrepareForTest
>@RunWith(PowerMockRunner.class)
>@PrepareForTest({RequestUtils.class)
@RunWith(PowerMockRunner.class)
语句告诉JUnit用PowerMockRunner来执行测试。@PrepareForTest(Employee.class)
语句告诉PowerMock准备Employee类进行测试。适用于模拟final类或有final, private, static, native方法的类@PrepareForTest
是当使用PowerMock强大的Mock静态、final、private方法时,需要添加的注解。
如果测试用例里没有使用注解@PrepareForTest
,可以不加注解@RunWith(PowerMockRunner.class)
,反之亦然。
2. 测试static方法
待测试类:Employee
public class Employee {
public static int count() {
throw new UnsupportedOperationException();
}
}
public class EmployeeService {
public int getEmployeeCount() {
return Employee.count();
}
}
对应的测试类:EmployeeServiceTest
@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());
}
}
注意这里使用的是mockStatic而不是的mock。
3. 测试返回void的静态方法
待测试类:EmployeeService
public class Employee {
public static void giveIncrementOf(int percentage) {
throw new UnsupportedOperationException();
}
}
public class EmployeeService{
public boolean giveIncrementToAllEmployeesOf(int percentage) {
try{
Employee.giveIncrementOf(percentage);
return true;
} catch(Exception e) {
return false;
}
}
}
测试类:EmployeeServiceTest
@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));
}
}
4. PowerMockito.doNothing 与PowerMockito.doThrow
PowerMockito.doNothing
方法告诉PowerMock下一个方法调用时什么也不做。
>`PowerMockito.doThrow`方法告诉PowerMock下一个方法调用时产生异常。
PowerMock使用自定义类加载器和字节码操作来模拟静态方法。对于实例中没有mock的方法,也有默认返回值,比如返回int类型的方法,默认返回0
PowerMockito.doNothing和PowerMockito.doThrow的语法可用于实例方法。待测试类:Employee
public class Employee{
public void save() {
throw new UnsupportedOperationException();
}
}
测试类:EmployeeTest
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方法不会对下一行产生影响。
5. 验证方法调用
> **待测试类:EmployeeService**
public class EmployeeService{
public void saveEmployee(Employee employee) {
if(employee.isNew()) {
employee.create();
return;
}
employee.update();
}
}
public class Employee{
public boolean isNew() {
throw new UnsupportedOperationException();
}
public void update() {
throw new UnsupportedOperationException();
}
public void create() {
throw new UnsupportedOperationException();
}
public static void giveIncrementOf(int percentage) {
throw new UnsupportedOperationException();
}
}
测试类:EmployeeServiceTest
public class EmployeeServiceTest {
@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方法。验证静态方法
public class EmployeeServiceTest {
@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);
}
}
6. 验证调用次数的方法:
Mockito.times(int n)
: 准确的验证方法调用的次数:nMockito.atLeastOnce()
: 验证方法至少调用1次Mockito.atLeast(int n)
: 验证方法最少调用n次Mockito.atMost(int n)
: 验证方法最多调用n次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();
}
-
测试final类或方法
待测试类:EmployeeGenerator
public final class EmployeeIdGenerator {
public final static int getNextId() {
throw new UnsupportedOperationException();
}
}
public class Employee {
public void setEmployeeId(int nextId) {
throw new UnsupportedOperationException();
}
}
public class EmployeeService {
public void saveEmployee(Employee employee) {
if(employee.isNew()) {
employee.setEmployeeId(EmployeeIdGenerator.getNextId());
employee.create();
return;
}
employee.update();
}
}
测试类:EmployeeServiceTest
@RunWith(PowerMockRunner.class)
@PrepareForTest(EmployeeIdGenerator.class)
public class EmployeeServiceTest {
@Test
public void shouldGenerateEmployeeIdIfEmployeeIsNew() {
Employee mock = PowerMockito.mock(Employee.class);
PowerMockito.when(mock.isNew()).thenReturn(true);
PowerMockito.mockStatic(EmployeeIdGenerator.class);
PowerMockito.when(EmployeeIdGenerator.getNextId()).thenReturn(90);
EmployeeService employeeService = new
EmployeeService();
employeeService.saveEmployee(mock);
PowerMockito.verifyStatic();
EmployeeIdGenerator.getNextId();
Mockito.verify(mock).setEmployeeId(90);
Mockito.verify(mock).create();
}
}
8. 测试构造方法
待测试类:WelcomeEmail
public class WelcomeEmail {
public WelcomeEmail(final Employee employee, final String message) {
throw new UnsupportedOperationException();
}
public void send() {
throw new UnsupportedOperationException();
}
}
public class EmployeeService{
public void saveEmployee(Employee employee) {
if(employee.isNew()) {
employee.setEmployeeId(EmployeeIdGenerator.getNextId());
employee.create();
WelcomeEmail emailSender = new WelcomeEmail(employee,
"Welcome to Mocking with PowerMock How-to!");
emailSender.send();
return;
}
employee.update();
}
}
测试类:WelcomeEmailTest
public class WelcomeEmailTest {
@RunWith(PowerMockRunner.class)
@PrepareForTest({EmployeeIdGenerator.class, EmployeeService.class})
public class EmployeeServiceTest {
@Test
public void shouldSendWelcomeEmailToNewEmployees()throws Exception {
Employee employeeMock =PowerMockito.mock(Employee.class);
PowerMockito.when(employeeMock.isNew()).thenReturn(true);
PowerMockito.mockStatic(EmployeeIdGenerator.class);
WelcomeEmail welcomeEmailMock = PowerMockito.mock(WelcomeEmail.class);
PowerMockito.whenNew(WelcomeEmail.class).withArguments(employeeMock, "Welcome").thenReturn(welcomeEmailMock);
EmployeeService employeeService = new EmployeeService();
employeeService.saveEmployee(employeeMock);
PowerMockito.verifyNew(WelcomeEmail.class).withArguments(employeeMock, "Welcome");
Mockito.verify(welcomeEmailMock).send();
}
}
}
注意PowerMockito.verifyNew的第2个参数支持前面提到的验证模式。>
PowerMockito.whenNew().withArguments(...).thenReturn()
是对构造方法的mock模式PowerMockito.verifyNew().withArguments()
是验证模式。
9. 参数匹配
PowerMock使用equals方法验证参数。matcher可更加灵活的处理参数。
Mockito.eq
;Mockito.matches
;Mockito.any
(anyBoolean , anyByte , anyShort , anyChar , anyInt ,anyLong , anyFloat , anyDouble , anyList , anyCollection , anyMap , anySet~);Mockito.isNull
;Mockito.isNotNull
;Mockito.endsWith
;Mockito.isA
;
10. 回答
在某些边缘的情况下不可能通过简单地通过PowerMockito.when().thenReturn()模拟,这时可以使用Answer接口。
@Test
public void shouldReturnCountOfEmployeesFromTheServiceWithDefaultAnswer() {
EmployeeService mock = PowerMockito.mock(EmployeeService.class, new Answer() {
public Object answer(InvocationOnMock invocation) {
return 10;
}
});
}
}
Answer接口指定执行的action和返回值执。
Answer的参数是InvocationOnMock的实例,支持:
callRealMethod()
:调用真正的方法getArguments()
:获取所有参数getMethod()
:返回mock实例调用的方法getMock()
:获取mock实例
11. spy进行部分模拟
待测试类:EmployeeService
public class EmployeeService{
public void saveEmployee(Employee employee) {
if(employee.isNew()) {
createEmployee(employee);
return;
}
employee.update();
}
void createEmployee(Employee employee) {
employee.setEmployeeId(EmployeeIdGenerator.getNextId());
employee.create();
WelcomeEmail emailSender = new WelcomeEmail(employee,
"Welcome");
emailSender.send();
}
}
测试类:EmployeeServiceTest
public class EmployeeServiceTest {
@Test
public void shouldInvokeTheCreateEmployeeMethodWhileSavingANewEmployee() {
final EmployeeService spy = PowerMockito.spy(new EmployeeService());
final Employee employeeMock = PowerMockito.mock(Employee.class);
PowerMockito.when(employeeMock.isNew()).thenReturn(true);
PowerMockito.doNothing().when(spy).createEmployee(employeeMock);
spy.saveEmployee(employeeMock);
Mockito.verify(spy).createEmployee(employeeMock);
}
}
spy只能使用PowerMockito.doNothing()/doReturn()/doThrow()。
12. 模拟私有方法
待测试类:EmployeeService
public class EmployeeService{
private void createEmployee(Employee employee) {
employee.setEmployeeId(EmployeeIdGenerator.getNextId());
employee.create();
WelcomeEmail emailSender = new WelcomeEmail(employee,
"Welcome");
emailSender.send();
}
}
测试类:EmployeeServiceTest
public class EmployeeServiceTest{
@RunWith(PowerMockRunner.class)
@PrepareForTest({EmployeeIdGenerator.class, EmployeeService.class})
public class EmployeeServiceTest {
@Test
public void shouldInvokeTheCreateEmployeeMethodWhileSavingANewEmployee() throws Exception {
final EmployeeService spy = PowerMockito.spy(new EmployeeService());
final Employee employeeMock = PowerMockito.mock(Employee.class);
PowerMockito.when(employeeMock.isNew()).thenReturn(true);
PowerMockito.doNothing().when(spy, "createEmployee", employeeMock);
spy.saveEmployee(employeeMock);
PowerMockito.verifyPrivate(spy).invoke("createEmployee", employeeMock);
}
}
}
PowerMockito.suppress(PowerMockito.constructor(BaseEntity.class))
:表示禁用BaseEntity的构造函数。
PowerMockito.suppress(PowerMockito.constructor(BaseEntity.class, String.class, Integer.class))
:后面表示带字符串和整数参数。
PowerMockito.suppress(PowerMockito.method(BaseEntity.class, "performAudit", String.class))
:表示禁用BaseEntity的performAudit方法。
@SuppressStaticInitializationFor("BaseEntity")
:表示禁用BaseEntity的静态初始化。注意引号部分通常需要全名,如”com.gitshah.powermock.BaseEntity”。
PowerMockito.suppress(PowerMockito.field(BaseEntity.class
:identifier”))`:禁用域。