安装
下载地址: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(); }