其他 mock 框架:powermock、easymock、jmock
mockito 官网:https://site.mockito.org
mockito 是一个单元测试框架。
Mockito 快速入门
下面来演示 Mockito 的入门案例。
包结构如下:
代码如下:
实体类 Account.java:
public class Account {
}
dao 层 AccountDao.java,该类中有一个接口,并且模拟数据库宕机的情况:
public class AccountDao {
public Account findAccount(String username, String password) {
throw new UnsupportedOperationException();
}
}
controller 层 AccountLoginController.java,该类中有一个接口:
public class AccountLoginController {
private final AccountDao accountDao;
public AccountLoginController(AccountDao accountDao) {
this.accountDao = accountDao;
}
public String login(HttpServletRequest request) {
final String userName = request.getParameter("username");
final String password = request.getParameter("password");
try {
Account account = accountDao.findAccount(userName, password);
if (account == null) {
return "/login";
} else {
return "/index";
}
} catch (Exception e) {
return "/505";
}
}
}
此时 controller 层的接口 login() 已经定义好了,现在要对其进行单元测试,但没有此时这个程序的环境中没有 servlet 容器,也没有数据库,如何进行测试?用 mockito !
AccountLoginControllerTest.java:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import javax.servlet.http.HttpServletRequest;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class AccountLoginControllerTest {
private AccountDao accountDao;
private HttpServletRequest request;
private AccountLoginController accountLoginController;
@Before
public void setUp() {
this.accountDao = Mockito.mock(AccountDao.class);
this.request = Mockito.mock(HttpServletRequest.class);
this.accountLoginController = new AccountLoginController(accountDao);
}
@Test
public void testLoginSuccess() {
Account account = new Account();
when(request.getParameter("username")).thenReturn("john");
when(request.getParameter("password")).thenReturn("123456");
when(accountDao.findAccount(anyString(), anyString())).thenReturn(account);
assertThat(accountLoginController.login(request), equalTo("/index"));
}
@Test
public void testLoginFailure() {
when(request.getParameter("username")).thenReturn("john");
when(request.getParameter("password")).thenReturn("123456");
when(accountDao.findAccount(anyString(), anyString())).thenReturn(null);
assertThat(accountLoginController.login(request), equalTo("/login"));
}
@Test
public void testLogin505() {
when(request.getParameter("username")).thenReturn("john");
when(request.getParameter("password")).thenReturn("123456");
when(accountDao.findAccount(anyString(), anyString())).thenThrow(UnsupportedOperationException.class);
assertThat(accountLoginController.login(request), equalTo("/505"));
}
}
三种不同的 mock 方式
方式一:使用 @RunWith 注解
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.mock;
@RunWith(MockitoJUnitRunner.class)
public class MockByRunnerTest {
@Test
public void testMock() {
AccountDao accountDao = mock(AccountDao.class);
Account account = accountDao.findAccount("x", "x");
System.out.println(account);
}
}
打印结果:
null
如果不希望 account 为 null,可以将代码修改如下:
@Test
public void testMock() {
AccountDao accountDao = mock(AccountDao.class, Mockito.RETURNS_SMART_NULLS);
Account account = accountDao.findAccount("x", "x");
System.out.println(account);
}
此时打印结果:
SmartNull returned by this unstubbed method call on a mock:
accountDao.findAccount("x", "x");
方式二:@Mock 注解
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class MockByAnnotationTest {
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Mock
private AccountDao accountDao;
@Test
public void testMock() {
Account account = accountDao.findAccount("x", "x");
System.out.println(account);
}
}
打印结果:
null
同样,若不想 account 为 null,可按如下编码:
import org.junit.Before;
import org.junit.Test;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class MockByAnnotationTest {
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Mock(answer = Answers.RETURNS_SMART_NULLS)
private AccountDao accountDao;
@Test
public void testMock() {
Account account = accountDao.findAccount("x", "x");
System.out.println(account);
}
}
打印结果:
SmartNull returned by this unstubbed method call on a mock:
accountDao.findAccount("x", "x");
方式三:@Rule 注解
import org.junit.Rule;
import org.junit.Test;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import static org.mockito.Mockito.mock;
public class MockByRuleTest {
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Test
public void testMock() {
AccountDao accountDao = mock(AccountDao.class);
Account account = accountDao.findAccount("x", "x");
System.out.println(account);
}
}
打印结果:
null
深度 Mock(Deep Mock)
下面的例子中使用 @Mock 注解来进行单元测试:
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.when;
public class DeepMockTest {
@Mock
private PersonService personService;
@Mock
private Person person;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testDeepMock() {
Person person = personService.get();
person.foo();
}
}
此时 testDeepMock() 测试无法通过,会报空指针异常,因为 person 为 null。
为了改进这个问题,将单元测试定义如下:
@Test
public void testDeepMock2() {
when(personService.get()).thenReturn(person);
Person person = personService.get();
person.foo();
}
此时可以通过测试,完整代码如下:
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.when;
public class DeepMockTest {
@Mock
private PersonService personService;
@Mock
private Person person;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testDeepMock() {
Person person = personService.get();
person.foo();
}
@Test
public void testDeepMock2() {
when(personService.get()).thenReturn(person);
Person person = personService.get();
person.foo();
}
}
对于上面代码,可以通过 Deep Mock 方式进行代码简化。简化后的代码如下:
import org.junit.Before;
import org.junit.Test;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class DeepMockTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PersonService personService;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testDeepMock() {
personService.get().foo();
}
}
Mockito Stubbing 语法详解
#未完待续
参考教程:汪文君Mockito实战视频
|