一、常用注解
@PrepareForTest和@RunWith是成对出现的,一般@RunWith(PowerMockRunner.class),@PrepareForTest的值是引用的静态方法或私有方法的类。
@InjectMocks注解标注的类会被注入所有被@Mock注解标注的类。
@Before注解用于方法,表示在执行@Test注解标注的方法之前执行。
在initMocks()方法中可以执行MockitoAnnotations.initMocks(this);来将@Mock注解修饰的类注入到@InjectMocks修饰的类。
二、实例操作
首先定义三个类,分别是实体类Employee.java,数据操作类EmployeeDao.java,业务逻辑类EmployeeService.java,下面是3个类的具体定义:
public class Employee {
private String name;
private Double salary;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the salary
*/
public Double getSalary() {
return salary;
}
/**
* @param salary the salary to set
*/
public void setSalary(Double salary) {
this.salary = salary;
}
}
EmployeeDao.java:
public interface EmployeeDao {
public Employee getEmployee(String name);
public boolean updateEmployee(Employee employee);
public static void deleteEmployee(String name) {
throw new NullPointerException();
}
public Exception saveEmployee(Employee employee);
}
EmployeeService.java:
public class EmployeeServiceImpl {
private EmployeeDao employeeDao;
public EmployeeServiceImpl() {
}
public EmployeeServiceImpl(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
public Employee getEmployee(String name) {
return employeeDao.getEmployee(name);
}
public boolean updateEmployee(Employee employee) {
return employeeDao.updateEmployee(employee);
}
public boolean deleteEmployee(String name) {
try {
EmployeeDao.deleteEmployee(name);
return true;
} catch (Exception e) {
return false;
}
}
public boolean saveEmployee(Employee employee) {
try {
employeeDao.saveEmployee(employee);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
下面是测试类中涉及到的PowerMockito知识点:
1)其中涉及到静态方法,因此需要使用@RunWith(PowerMockRunner.class)
@PrepareForTest({ EmployeeDao.class })注解,PrepareForTest的值是静态方法所在的类或接口,否则会报下面的错误:
org.powermock.api.mockito.ClassNotPreparedException:
The class mock.EmployeeDao not prepared for test.
To prepare this class, add class to the '@PrepareForTest' annotation.
In case if you don't use this annotation, add the annotation on class or method level.
at org.powermock.api.mockito.expectation.reporter.MockitoPowerMockReporter.classNotPrepared(MockitoPowerMockReporter.java:32)
2)使用@Before注解在调用@Test注解的方法之前进行初始化,比如使用mock(类的Class对象)来创建需要模拟的对象,这样我们就可以假设被模拟的对象已经实现,尽管他其实是一个没有实现类的接口。
3)在模拟对象创建之后,我们就可以使用when(调用模拟对象的方法).thenReturn(定义方法调用的返回值),这样我们就相当于假设调用被模拟对象的方法后返回的一直是thenReturn中定义的值。这样我们就不用关心被模拟对象的实现按,只关心下面我们测试的业务逻辑是否存在问题。
@RunWith(PowerMockRunner.class)
@PrepareForTest({ EmployeeDao.class })
public class EmployeeServiceImplTest {
@Mock
public EmployeeDao employeeDao;
@Mock
EmployeeServiceImpl employeeServiceImpl;
// @Before注解的方法会在调用测试方法前执行初始化动作
@Before
public void initMocks() {
// 创建模拟对象EmployeeDao的实例
employeeDao = mock(EmployeeDao.class);
// 将模拟对象赋给业务类实例
employeeServiceImpl = new EmployeeServiceImpl(employeeDao);
}
@Test
public void getEmployeeTest() {
String name = "scott";
Employee employee = new Employee();
employee.setName("scott");
employee.setSalary(8888.0);
// 定义当执行employeeDao.getEmployee(name)方法时始终返回employee对象,相当于实现了employeeDao的这个方法
when(employeeDao.getEmployee(name)).thenReturn(employee);
/*
* 下面测试我们想要单元测试的employeeServiceImpl.getEmployee(name)方法 我们已经屏蔽了该方法对employeeDao.getEmployee(name)的调用,相当于解除了依赖
* 这样我们只需要关心employeeServiceImpl.getEmployee(name)方法的逻辑是否存在问题
*/
Employee employee2 = employeeServiceImpl.getEmployee(name);
System.out.println(employee2.getSalary());
}
@Test
public void updateEmployeeTest() {
Employee employee = new Employee();
employee.setName("tiger");
employee.setSalary(99999.0);
when(employeeDao.updateEmployee(anyObject())).thenReturn(true);
Employee employee2 = new Employee();
employee.setName("scott");
employee.setSalary(99999.0);
Boolean boolean1 = employeeServiceImpl.updateEmployee(employee2);
System.out.println(boolean1);
}
@Test
public void deleteEmployeeTest() {
String name = "haha";
// 因为这里调用的是静态方法,因此使用PowerMockito.mockStatic(EmployeeDao.class);来模拟静态类
PowerMockito.mockStatic(EmployeeDao.class);
// 使用doNothing()定义执行下面一句语句时什么也不做
PowerMockito.doNothing().when(EmployeeDao.class);
// 这一句由于上面的doNothing()即使会抛异常也不会再抛
EmployeeDao.deleteEmployee(name);
// 因此employeeServiceImpl.deleteEmployee(name)执行时也没有发现异常返回true
EmployeeServiceImpl employeeServiceImpl = new EmployeeServiceImpl();
Assert.assertTrue(employeeServiceImpl.deleteEmployee(name));
}
@Test
public void throwDeleteExceptionTest() {
String name = "haha";
PowerMockito.mockStatic(EmployeeDao.class);
// doThrow()定义下面一句语句会抛出异常
PowerMockito.doThrow(new NullPointerException()).when(EmployeeDao.class);
EmployeeDao.deleteEmployee(name);
// 因此employeeServiceImpl.deleteEmployee(name)执行会返回false
EmployeeServiceImpl employeeServiceImpl = new EmployeeServiceImpl();
assertTrue(employeeServiceImpl.deleteEmployee(name));
}
@Test
public void saveEmployeeTest() {
Employee employee = new Employee();
employee.setName("scott");
employee.setSalary(8888.0);
// 打桩,定义方法返回的值
when(employeeDao.saveEmployee(employee)).thenReturn(new NullPointerException());
// 这里doNothing()没有生效,原因是这里调用的是实例方法,不是静态方法
PowerMockito.doNothing().when(employeeDao).saveEmployee(employee);
employeeServiceImpl.saveEmployee(employee);
}
@Test
public void throwSaveEmployeeTest() {
Employee employee = new Employee();
employee.setName("scott");
employee.setSalary(8888.0);
when(employeeDao.saveEmployee(employee)).thenReturn(new NullPointerException());
// 这里的doThrow()实际没有生效,因为这里调用的是实例方法,不是静态方法,因此不存在使下一句语句抛异常的作用
PowerMockito.doThrow(new NullPointerException()).when(employeeDao).saveEmployee(employee);
employeeServiceImpl.saveEmployee(employee);
}
}