目录
什么是 Mock 测试?
Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。
Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。
1 如何使用Mockito?
添加两段依赖
第一段依赖是单元测试框架JUnit ,关于JUnit框架可以参考:https://blog.csdn.net/qq_36110736/article/details/107774507
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
下面这部分就是Mockito的依赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
2 实际使用
此处以一个Spring Boot 工程为测试工程,模拟一次用户登陆行为(只是简单的测试,所以整体逻辑并不完善)。
2.1 准备工作
首先创建三个类 PO、DAO、Conreoller.
PO:其实是用不到其中的Setter/Getter 方法的。
package com.example.demo.po;
public class UserLogin {
private String userName;
private String passWord;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
}
DAO:直接抛出了异常,没有具体的实现
package com.example.demo.dao;
import com.example.demo.po.UserLoin;
public class LoginDao {
public UserLoin isExit(String username,String password){
throw new UnsupportedOperationException();
}
}
Controller:重写构造方法,将LoginDao 作为参数,实现了简单的逻辑:正常登陆,未找到用户,以及Dao层异常处理
package com.example.demo.controller;
import com.example.demo.dao.LoginDao;
import com.example.demo.po.UserLoin;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
public class LoginController {
private final LoginDao loginDao;
public LoginController(LoginDao loginDao) {
this.loginDao = loginDao;
}
public String Login(HttpServletRequest request){
final String userName = request.getParameter("username");
final String password = request.getParameter("password");
try {
UserLogin userLogin = loginDao.isExit(userName,password);
if (userLogin == null){
return "login";
}else {
return "index.jsp";
}
}catch (Exception e){
return "505";
}
}
}
2.2 开始测试
大致流程:
- 修改 @RunWith() 为 MockitoJUnitRunner.class
- 添加三个会用到属性
- 在 @Before 修饰的方法中 使用 mock 进行初始化
- 分别创建三个方法对三种情况(未找到用户、正常登陆、异常)进行测试
package com.example.demo.controller;
import com.example.demo.dao.LoginDao;
import com.example.demo.po.UserLogin;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.http.HttpServletRequest;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class LoginControllerTest {
private LoginDao loginDao;
private HttpServletRequest request;
private LoginController controller;
@Before
public void setUp() {
this.loginDao = Mockito.mock(LoginDao.class);
this.request = Mockito.mock(HttpServletRequest.class);
this.controller = new LoginController(loginDao);
}
@Test
public void testLoginFailure() {
when(request.getParameter("username")).thenReturn("bysen");
when(request.getParameter("password")).thenReturn("123456");
when(loginDao.isExit(anyString(),anyString())).thenReturn(null);
Assert.assertEquals("login",controller.Login(request));
}
@Test
public void testLoginSuccess(){
UserLogin login = new UserLogin();
when(request.getParameter("username")).thenReturn("bysen");
when(request.getParameter("password")).thenReturn("123456");
when(loginDao.isExit(anyString(),anyString())).thenReturn(login);
Assert.assertEquals("index.jsp",controller.Login(request));
}
@Test
public void testLogin505(){
when(request.getParameter("username")).thenReturn("bysen");
when(request.getParameter("password")).thenReturn("123456");
when(loginDao.isExit(anyString(),anyString())).thenThrow(UnsupportedOperationException.class);
Assert.assertEquals("505",controller.Login(request));
}
}
2.3 测试结果
3.几种Mock方式
3.1 借助Runwith
借助Junit的 @Runwith ,将参数设置为 MockitoJUnitRunner.class,例如:
package com.example.demo.test;
import com.example.demo.dao.LoginDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class MockitoRunnerTest {
@Test
public void test(){
LoginDao loginDao = mock(LoginDao.class);
loginDao.isExit(anyString(),anyString());
}
}
3.2 Annotation
- 在测试类中创建一个用 @Before 修饰的方法,
- 在其中添加 MockitoAnnotations.initMocks(this),
- 就可以直接使用@Mock注解mock对象。
package com.example.demo.test;
import com.example.demo.dao.LoginDao;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.ArgumentMatchers.anyString;
public class MockByAnnotationTest {
@Mock(answer = Answers.RETURNS_SMART_NULLS)
private LoginDao loginDao;
@Before
public void before(){
MockitoAnnotations.initMocks(this);
}
@Test
public void test(){
loginDao.isExit(anyString(),anyString());
}
}
3.3 借助Rule(不推荐)
借助 MockitoJUnit.rule() 方法
package com.example.demo.test;
import com.example.demo.dao.LoginDao;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import static org.mockito.ArgumentMatchers.anyString;
public class MockByRuleTest {
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock
private LoginDao loginDao;
@Test
public void test(){
loginDao.isExit(anyString(),anyString());
}
}
3.4 Deepmock
一般来说,我们mock 出来的对象并不能完全代替原有的对象,在mock对象执行一些方法时很容易出现java.lang.NullPointerException,此时就需要Deepmock 来对特定操作返回特定值。
首先我们在Dao类中添加一个方法,返回一个空值。
public UserLogin getUser(){
return null;
}
然后:正常的mock对象,
package com.example.demo.test;
import com.example.demo.dao.LoginDao;
import com.example.demo.po.UserLogin;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
public class DeepMockTest {
@Mock
private LoginDao loginDao;
@Mock
private UserLogin user;
@Before
public void before(){
MockitoAnnotations.initMocks(this);
}
@Test
public void test(){
// Mockito.when(loginDao.getUser()).thenReturn(user);
UserLogin userLogin = loginDao.getUser();
System.out.println("=======");
System.out.println(userLogin);
System.out.println("=======");
}
}
如果不使用 when().thenReturn()的话,结果为null。
使用的话: