单元测试之PowerMock

为什么使用PowerMock

前面2篇文章介绍了JunitMockito。其中,Junit是一个简单的单元测试框架,Mockito是简单的用于mock的单元测试框架。 
EasyMock、Mockito等通过创建Proxy实现mock,不能mock static、final、private方法,测试存在局限性。PowerMock使用CGLi操纵字节码mock对象,可以mock static、final、private方法。


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) : 准确的验证方法调用的次数:n 
Mockito.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();
}
  1. 测试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”))`:禁用域。


count
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值