单元测试之PowerMock

为什么使用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) : 准确的验证方法调用的次数: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”))`:禁用域。

猜你喜欢

转载自blog.csdn.net/linjpg/article/details/89263390
今日推荐