Mocking is a way to test the functionality of a class in isolation. Mocking does not require a database connection or properties file read or file server read to test a functionality. Mock objects do the mocking of the real service. A mock object returns a dummy data corresponding to some dummy input passed to it.
Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。
业务代码:
@Getter
@Setter
public class Account {
private String username;
private String password;
}
public class AccountDao {
public Account findAccount(String username, String password) {
throw new UnsupportedOperationException();
}
}
public class LoginController {
private AccountDao accountDao;
public LoginController(AccountDao accountDao) {
this.accountDao = accountDao;
}
public String login(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
try {
Account account = accountDao.findAccount(username, password);
if (account == null)
return "/login";
} catch (Exception e) {
return "/505";
}
return "/index";
}
}
单元测试:
public class LoginControllerTest {
private AccountDao accountDao;
private HttpServletRequest request;
private LoginController loginController;
@Before
public void before() throws Exception {
accountDao = Mockito.mock(AccountDao.class);
request = Mockito.mock(HttpServletRequest.class);
loginController = new LoginController(accountDao);
}
@After
public void after() throws Exception {
}
/**
* Method: login(HttpServletRequest request)
*/
@Test
public void testLoginSuccess() throws Exception {
when(request.getParameter("username")).thenReturn("jimGreen");
when(request.getParameter("password")).thenReturn("123456");
when(accountDao.findAccount(anyString(), anyString())).thenReturn(new Account());
assertThat(loginController.login(request), equalTo("/index"));
}
@Test
public void testLoginFail() throws Exception {
when(request.getParameter("username")).thenReturn("jimGreen");
when(request.getParameter("password")).thenReturn("123456xx");
when(accountDao.findAccount(anyString(), anyString())).thenReturn(null);
assertThat(loginController.login(request), equalTo("/login"));
}
@Test
public void testLoginException() throws Exception {
when(request.getParameter("username")).thenReturn("jimGreen");
when(request.getParameter("password")).thenReturn("123456xx");
when(accountDao.findAccount(anyString(), anyString())).thenThrow(new UnsupportedOperationException());
assertThat(loginController.login(request), equalTo("/505"));
}
}
用到的业务代码示例:
domain:
第一种方式:使用Runner的方式
@RunWith(MockitoJUnitRunner.class)
public class MockByRunner {
private AccountDao accountDao;
@Test
public void testMock(){
accountDao = mock(AccountDao.class, Mockito.RETURNS_SMART_NULLS);
Account account = accountDao.findAccount("x", "x");
System.out.println(account);
}
}
第二种方式:使用Annotation的方式
public class MockByAnnotation {
@Mock(answer = Answers.RETURNS_SMART_NULLS)
AccountDao accountDao;
@Before
public void before() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testMockByAnnotation() {
Account account = accountDao.findAccount("x", "x");
System.out.println(account);
}
}
第三种方式:使用Rule的方式
public class MockByMockRule {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock(answer = Answers.RETURNS_SMART_NULLS)
private AccountDao dao;
@Test
public void testMockRule(){
Account account = dao.findAccount("x", "x");
System.out.println(account);
}
}
DeepMock
public class DeepMock {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Lession03Service service;
@Before
public void before(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testDeepMock(){
Lession03 lession03 = service.get();
lession03.foo();
}
}
@RunWith(MockitoJUnitRunner.class)
public class SpyingTest {
@Test
public void testSpy() {
List<String> realList = new ArrayList<>();
List<String> list = spy(realList);
list.add("mockito");
list.add("powermock");
assertThat(list.get(0), equalTo("mockito"));
assertThat(list.get(1), equalTo("powermock"));
assertThat(list.size(), equalTo(2));
assertThat(list.isEmpty(), equalTo(false));
when(list.isEmpty()).thenReturn(true);
assertThat(list.get(0), equalTo("mockito"));
assertThat(list.get(1), equalTo("powermock"));
assertThat(list.size(), equalTo(2));
assertThat(list.isEmpty(), equalTo(true));
}
}
使用annotation进行spy
public class SpyingAnnotation {
@Spy
private List<String> list = new ArrayList<>();
@Before
public void before(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testSpyingWithAnnotation(){
list.add("mockito");
list.add("powermock");
assertThat(list.get(0), equalTo("mockito"));
assertThat(list.get(1), equalTo("powermock"));
assertThat(list.isEmpty(), equalTo(false));
when(list.isEmpty()).thenReturn(true);
when(list.size()).thenReturn(0);
assertThat(list.get(0), equalTo("mockito"));
assertThat(list.get(1), equalTo("powermock"));
assertThat(list.isEmpty(), equalTo(true));
assertThat(list.size(), equalTo(0));
}
}
业务代码:
public class StubbingService {
public int getI(){
System.out.println("=====getI=====");
return 10;
}
public String getS(){
System.out.println("=====getS=====");
throw new RuntimeException();
}
}
单元测试:
public class StubbingTest {
private List<String> list;
@Before
public void before() {
list = Mockito.mock(ArrayList.class);
}
@Test
public void howToUseStubbing() {
when(list.get(0)).thenReturn("first");
assertThat(list.get(0), equalTo("first"));
when(list.get(anyInt())).thenThrow(RuntimeException.class);
try {
list.get(0);
fail();
} catch (Exception e) {
assertThat(e, instanceOf(RuntimeException.class));
}
}
@Test
public void howToStubbingVoidMethod() {
Mockito.doNothing().when(list).clear();
list.clear();
verify(list, times(1)).clear();
doThrow(RuntimeException.class).when(list).clear();
try {
list.clear();
fail();
} catch (Exception e) {
assertThat(e, instanceOf(RuntimeException.class));
}
}
@Test
public void stubbingDoReturn() {
when(list.get(0)).thenReturn("first");
doReturn("second").when(list).get(1);
assertThat(list.get(0), equalTo("first"));
assertThat(list.get(1), equalTo("second"));
}
@Test
public void iterateStubbing() {
// when(list.size()).thenReturn(1,2,3,4);
when(list.size()).thenReturn(1).thenReturn(2).thenReturn(3).thenReturn(4);
assertThat(list.size(), equalTo(1));
assertThat(list.size(), equalTo(2));
assertThat(list.size(), equalTo(3));
assertThat(list.size(), equalTo(4));
assertThat(list.size(), equalTo(4));
}
@Test
public void stubbingWithAnswer() {
when(list.get(anyInt())).thenAnswer(invocationOnMock -> {
Integer index = invocationOnMock.getArgumentAt(0, Integer.class);
return String.valueOf(index * 10);
});
assertThat(list.get(0), equalTo("0"));
assertThat(list.get(99), equalTo("990"));
}
@Test
public void stubbingWithRealCall() {
StubbingService service = mock(StubbingService.class);
when(service.getS()).thenReturn("hello");
when(service.getI()).thenCallRealMethod();
assertThat(service.getS(), equalTo("hello"));
assertThat(service.getI(), equalTo(10));
}
@After
public void after() {
Mockito.reset(this.list);
}
}
public class ArgumentMatcherTest {
@Test
public void testBasic() {
List<Integer> list = mock(ArrayList.class);
when(list.get(0)).thenReturn(100);
assertThat(list.get(0), equalTo(100));
assertThat(list.get(1), nullValue());
}
@Test
public void testComplex() {
Foo foo = mock(Foo.class);
when(foo.function(Mockito.isA(Parent.class))).thenReturn(100);
assertThat(foo.function(new Child1()), equalTo(100));
assertThat(foo.function(new Child2()), equalTo(100));
reset(foo);
when(foo.function(Mockito.any(Child1.class))).thenReturn(100);
assertThat(foo.function(new Child2()), equalTo(100));
}
static class Foo {
int function(Parent p) {
return p.work();
}
}
interface Parent {
int work();
}
static class Child1 implements Parent {
@Override
public int work() {
throw new RuntimeException();
}
}
static class Child2 implements Parent {
@Override
public int work() {
throw new RuntimeException();
}
}
}
@RunWith(MockitoJUnitRunner.class)
public class VerifyTest {
@Mock
private UserDao userDao;
@Test
public void testCallCreateuser() {
UserService userService = new UserService(userDao);
User user = new User("123456");
when(userDao.isExist(user)).thenReturn(false);
when(userDao.create(user)).thenReturn(true);
when(userDao.update(user)).thenReturn(true);
boolean result = userService.merge(user);
assertThat(result, equalTo(true));
verify(userDao, times(0)).update(user);
verify(userDao, times(1)).create(user);
}
@Test
public void testCallUpdateuser() {
UserService UserService = new UserService(userDao);
User user = new User("123456");
when(userDao.isExist(user)).thenReturn(true);
when(userDao.create(user)).thenReturn(true);
when(userDao.update(user)).thenReturn(true);
boolean result = UserService.merge(user);
assertThat(result, equalTo(true));
verify(userDao, times(1)).update(user);
verify(userDao, times(0)).create(user);
}
@Test
public void testMerge() {
UserService UserService = new UserService(userDao);
User user = new User("123456");
when(userDao.isExist(user)).thenReturn(false, true);
when(userDao.create(user)).thenReturn(true);
when(userDao.update(user)).thenReturn(true);
boolean result = UserService.merge(user);
assertThat(result, equalTo(true));
verify(userDao, times(0)).update(user);
verify(userDao, times(1)).create(user);
result = UserService.merge(user);
assertThat(result, equalTo(true));
verify(userDao, times(1)).update(user);
verify(userDao, times(1)).create(user);
}
@After
public void after() {
reset(userDao);
}
}
@RunWith(MockitoJUnitRunner.class)
public class ArgumentCaptorTest {
@Mock
private List<String> list;
@Mock
private UserDao userDao;
@Test
public void test() {
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
when(list.add("mockito")).thenReturn(true);
list.add("mockito");
verify(list, times(1)).add("mockito");
verify(list, times(1)).add(captor.capture());
assertThat(captor.getValue(), equalTo("mockito"));
}
@Test
public void test2() {
UserService userService = new UserService(userDao);
User user = new User("123456");
when(userDao.delete(user)).thenReturn(true);
assertThat(userService.deleteUser(user), equalTo(true));
assertThat(user.getType(), equalTo("D"));
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(userDao, times(1)).delete(captor.capture());
}
}