1.概述
Mockito 是一个针对 Java 的单元测试模拟框架,它与 EasyMock 和 jMock 很相似,都是为了简化单元测试过程中测试上下文的搭建而开发的工具。
PowerMock 也是一个单元测试模拟框架,它是在其它单元测试模拟框架的基础上做出的扩展。通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 现了对静态方法、构造方法、私有方法以及 Final 方法的模拟支持,对静态初始化过程的移除等强大的功能。
2.引入依赖
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
</dependency>
3.代码示例
单元测试用到的类:
public interface StudentDao {
String queryStudent();
}
public interface StudentService {
public String selectStudentByName();
public String selectStudentByName2();
}
@Repository
public class StudentDaoImpl implements StudentDao {
@Override
public String queryStudent() {
return "拿着核武器的程序员";
}
}
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
// 正常方法
@Override
public String selectStudentByName() {
// 调用了私有方法
String student = getNameById();
System.out.println("method:selectStudentByName" + student);
return student;
}
// 正常方法
@Override
public String selectStudentByName2() {
String name = StudentServiceImpl.selectStudentById();
System.out.println("静态方法返回值:" + name);
return name;
}
// 静态方法
public static String selectStudentById() {
System.out.println("------------------");
return "拿着大炮的男孩";
}
private String getNameById() {
String student = studentDao.queryStudent();
System.out.println("method:getNameById" + student);
return student;
}
public final String getAgeById() {
System.out.println("----------------");
return "拿着大炮的男孩";
}
}
单元测试类:
@RunWith(PowerMockRunner.class)
@PrepareForTest({ StudentServiceImpl.class })
public class StudentServiceTest {
/**
* spy()方法:表示局部mock,执行方法的时候会进入具体的方法内,spy的作用是让被测类正常工作,但是可以拦截某些方法的返回值
* 比如让StudentServiceImpl中的selectStudentByName()正常工作,但是返回值可以是我们期望的值
* mock()方法:表示全局mock,执行方法的时候不会进入到具体方法内
*/
/**
* 使用powermock的测试类需要加@RunWith(PowerMockRunner.class)
* 若需要模拟构造函数、私有方法、static方法、final方法都需要在测试类上添加注解
*
* @throws Exception
* @PrepareForTest({ 被Mock的类 })
*
* 1.测试私有方法
*/
@Test
public void test() throws Exception {
// 局部mock
StudentServiceImpl serviceImpl = PowerMockito.spy(new StudentServiceImpl());
// 在selectStudentByName中调用了私有方法,此时需要对私有方法mock
// 若有参数,就在后面使用Mockito.anyXxx()
// 由于私有方法中又使用了自动注入的StudentDao,所以mock StudentDao然后通过Whitebox(白盒)替换属性
StudentDao studentDao = PowerMockito.mock(StudentDao.class);
// 第一个参数:需要被替换属性的对象 ;第二个参数:属性名 ; 第三个参数:属性值
Whitebox.setInternalState(serviceImpl, "studentDao", studentDao);
PowerMockito.doReturn("hahahaha").when(studentDao).queryStudent();
PowerMockito.when(serviceImpl, "getNameById").thenReturn("东皇太一");
serviceImpl.selectStudentByName();
}
/**
* 2.测试静态方法
* 想要单元测试selectStudentByName2()方法,但是该方法中使用了静态方法
*/
@Test
public void test2() {
// 模拟静态方法前需要先调用这一句,不写会报错!!!
PowerMockito.mockStatic(StudentServiceImpl.class);
// 模拟当执行静态方法StudentServiceImpl.selectStudentById()时候,返回123(模拟的返回值)
PowerMockito.when(StudentServiceImpl.selectStudentById()).thenReturn("123");
StudentServiceImpl serviceImpl = PowerMockito.spy(new StudentServiceImpl());
serviceImpl.selectStudentByName2();
}
/**
* 3.测试final方法
*/
@Test
public void test3() {
StudentServiceImpl service = PowerMockito.mock(StudentServiceImpl.class);
PowerMockito.when(service.getAgeById()).thenReturn("18");
String age = service.getAgeById();
System.out.println(age);
}
}