本节主要介绍Mockito ,接上篇:https://www.cnblogs.com/javaXRG/p/17537364.html
1、基础用法
1.1 工程代码
1 package org.example.Domain; 2 3 import lombok.AllArgsConstructor; 4 import lombok.Data; 5 6 @Data 7 @AllArgsConstructor 8 public class User { 9 10 private Long id; 11 12 private String userName; 13 14 private String password; 15 }
1 package org.example.dao; 2 3 import org.example.Domain.User; 4 import org.springframework.stereotype.Service; 5 6 import java.util.HashMap; 7 import java.util.Map; 8 9 // dao层 10 @Service 11 public class UserDao { 12 13 public static Map<Long, User> cache = new HashMap<>(); 14 15 public User findUser(String userName, String password) { 16 // 执行查询操作 17 return null; 18 } 19 20 public void insert(User user) { 21 // 执行入库操作 22 } 23 24 public User findByCache(long id) { 25 return cache.get(id); 26 } 27 28 public void putIntoCache(User user) { 29 cache.put(user.getId(), user); 30 } 31 }
package org.example.service; import org.example.Domain.User; import org.example.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; // service层 @Service public class UserService { @Autowired private UserDao userDao; public User findUser(String userName, String password) { return userDao.findUser(userName, password); } public void insert(User user) { userDao.insert(user); } public User findByCache(long id) { return userDao.findByCache(id); } }
1 package org.example.controller; 2 3 import org.example.Domain.User; 4 import org.example.service.UserService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Controller; 7 8 @Controller 9 public class UserController { 10 11 @Autowired 12 private UserService userService; 13 14 public User findUser(String userName, String password) { 15 return userService.findUser(userName, password); 16 } 17 18 public void insert(User user) { 19 userService.insert(user); 20 } 21 }
1.2 单测
对service中的方法进行单测
1 package org.example.service; 2 3 import org.example.Domain.User; 4 import org.example.dao.UserDao; 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.mockito.ArgumentMatchers; 8 import org.mockito.InjectMocks; 9 import org.mockito.Mock; 10 import org.mockito.junit.MockitoJUnitRunner; 11 12 import static org.junit.Assert.*; 13 import static org.mockito.ArgumentMatchers.any; 14 import static org.mockito.Mockito.*; 15 16 @RunWith(MockitoJUnitRunner.class) 17 public class UserServiceTest { 18 19 @InjectMocks 20 private UserService userService; 21 22 // mock userDao(基于cgLib实现一个proxy对象) 23 @Mock 24 private UserDao userDao; 25 26 @Test 27 public void findUserTest() { 28 // 打桩 stubbing 29 when(userDao.findUser("zhangsan", "123456")).thenReturn(new User(1L, "zhangsan", "123456")); 30 // 执行方法 31 User user = userService.findUser("zhangsan", "123456"); 32 // 断言 33 assertNotNull(user); 34 assertEquals(1L, user.getId().longValue()); 35 assertEquals("zhangsan", user.getUserName()); 36 assertEquals("123456", user.getPassword()); 37 } 38 39 @Test 40 public void insertUserTest() { 41 // void方法采用doNothing().when().xxxMethod()语法 42 doNothing().when(userDao).insert(any(User.class)); 43 User user = new User(1L, "zhangsan", "123456"); 44 userService.insert(user); 45 verify(userDao).insert(user); 46 } 47 48 /** 49 * thenThrow方法抛出异常 50 */ 51 @Test(expected = RuntimeException.class) 52 public void throwTest() { 53 when(userDao.findUser("zhangsan", "123456")).thenThrow(new RuntimeException("用户名或密码错误")); 54 userService.findUser("zhangsan", "123456"); 55 } 56 57 /** 58 * void方法抛出异常 59 */ 60 @Test(expected = RuntimeException.class) 61 public void voidMethodThrowTest() { 62 doThrow(RuntimeException.class).when(userDao).insert(any(User.class)); 63 userService.insert(new User(1L, "zhangsan", "123456")); 64 } 65 }
1.3 复杂返回值构造
package org.example.service; import org.example.Domain.User; import org.example.dao.UserDao; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class UserServiceTest1 { @InjectMocks private UserService userService; @Mock private UserDao userDao; /** * 当返回值逻辑比较复杂时,可用thenAnswer进行编码式处理 */ @Test public void thenAnswerTest() { when(userDao.findUser(anyString(), anyString())).thenAnswer(invocationOnMock -> invocationOnMock.getArguments()[0].toString().startsWith("visitor") ? new User(100L, "visitor-1", "234567") : null ); assertNull(userService.findUser("zhangsan", "123456")); assertNotNull(userService.findUser("visitor-1", "234567")); } /** * thenReturn返回多个值 */ @Test public void thenReturnMoreTest() { when(userDao.findUser(anyString(), anyString())).thenReturn( new User(1L, "zhangsan", "123456"), new User(2L, "lisi", "23456"), new User(3L, "wangwu", "345678") ); // 每次调用返回 thenReturn对应序号结果 assertEquals(1L, userService.findUser("zhangsan", "123456").getId().intValue()); assertEquals(2L, userService.findUser("zhangsan", "123456").getId().intValue()); assertEquals(3L, userService.findUser("zhangsan", "123456").getId().intValue()); // 调用次数超过最大次数时,返回最后一个值 assertEquals(3L, userService.findUser("zhangsan", "123456").getId().intValue()); assertEquals(3L, userService.findUser("zhangsan", "123456").getId().intValue()); } }
1.4 复杂参数构造
package org.example.service; import org.assertj.core.util.Preconditions; import org.example.Domain.User; import org.example.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService1 { @Autowired private UserDao userDao; public void insertAndSendMessage(String userName) { // 局部变量 User user = new User(userName); userDao.insert(user); // 数据库自增id Long id = user.getId(); Preconditions.checkNotNull(id); // 发送mq消息 } }
1 package org.example; 2 3 import org.example.Domain.User; 4 import org.example.dao.UserDao; 5 import org.example.service.UserService1; 6 import org.junit.Ignore; 7 import org.junit.Test; 8 import org.junit.runner.RunWith; 9 import org.mockito.ArgumentMatcher; 10 import org.mockito.ArgumentMatchers; 11 import org.mockito.InjectMocks; 12 import org.mockito.Mock; 13 import org.mockito.junit.MockitoJUnitRunner; 14 15 import static org.mockito.ArgumentMatchers.anyString; 16 import static org.mockito.ArgumentMatchers.eq; 17 import static org.mockito.Mockito.*; 18 19 @RunWith(MockitoJUnitRunner.class) 20 public class ArgumentMatcherTest { 21 22 @InjectMocks 23 private UserService1 userService1; 24 25 @Mock 26 private UserDao userDao; 27 28 /** 29 * 该用例执行会报错,原因: 30 * 如果使用菜参数匹配,该次调用的所有参数必须是参数匹配方式 31 */ 32 @Test 33 @Ignore 34 public void argumentMatcherTest1() { 35 when(userDao.findUser("zhangsan", anyString())).thenReturn(new User(1L, "zhagnsan", "123456")); 36 } 37 38 /** 39 * 可使用eq()语法解决上述问题 40 */ 41 @Test 42 public void argumentMatcherTest2() { 43 when(userDao.findUser(eq("zhangsan"), anyString())).thenReturn(new User(1L, "zhagnsan", "123456")); 44 } 45 46 @Test 47 public void argumentMatcherTes3() { 48 when(userDao.findUser(ArgumentMatchers.argThat(new ArgumentMatcher<String>() { 49 @Override 50 public boolean matches(String s) { 51 // 判断参数是否匹配逻辑 52 return false; 53 } 54 }), anyString())).thenReturn(new User(1L, "zhangsan", "123456")); 55 } 56 57 @Test 58 public void argumentMatcherTes4() { 59 when(userDao.findUser(isA(String.class), isA(String.class))).thenReturn(new User(1L, "zhangsan", "123456")); 60 } 61 62 @Test 63 public void insertAndSendMessageTest() { 64 doNothing().when(userDao).insert(ArgumentMatchers.argThat(user -> { 65 user.setId(100L); 66 return true; 67 })); 68 userService1.insertAndSendMessage("zhangsan"); 69 verify(userDao, times(1)).insert(new User(100L, "zhangsan", null)); 70 } 71 }
1.5 mock对象执行真实方法
1 package org.example.service; 2 3 import org.example.Domain.User; 4 import org.example.dao.UserDao; 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.mockito.InjectMocks; 8 import org.mockito.Mock; 9 import org.mockito.junit.MockitoJUnitRunner; 10 11 import static org.junit.Assert.assertEquals; 12 import static org.mockito.ArgumentMatchers.any; 13 import static org.mockito.Mockito.*; 14 15 @RunWith(MockitoJUnitRunner.class) 16 public class UserServiceTest2 { 17 18 @InjectMocks 19 private UserService userService; 20 21 @Mock 22 private UserDao userDao; 23 24 /** 25 * mock对象创建的方法,都不会真正的执行原始方法 26 * 如果想执行原始方法,可以使用: 27 * when().thenCallRealMethod() 28 * 或 29 * doCallRealMethod().when().method() 30 */ 31 @Test 32 public void callRealMethodTest() { 33 doCallRealMethod().when(userDao).putIntoCache(any(User.class)); 34 when(userDao.findByCache(1L)).thenCallRealMethod(); 35 userDao.putIntoCache(new User(1L, "zhangsan", "123456")); 36 User user = userService.findByCache(1L); 37 assertEquals("zhangsan", user.getUserName()); 38 verify(userDao, times(1)).putIntoCache(user); 39 } 40 }
1.6 @InjectMocks + spy解决多层依赖问题
package org.example.controller; import org.example.Domain.User; import org.example.dao.UserDao; import org.example.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class UserControllerTest { @InjectMocks private UserController userController; /** * @InjectMocks + spy() 解决多层依赖问题 */ @InjectMocks private UserService userService = spy(UserService.class); @Mock private UserDao userDao; @Test public void findUser() { when(userDao.findUser(anyString(), anyString())).thenReturn(new User(1L, "zhangsan", "123456")); User user = userController.findUser("zhangsan", "123456"); assertEquals("zhangsan", user.getUserName()); } }
1.7 @Spy
package org.example; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.doReturn; @RunWith(MockitoJUnitRunner.class) public class SpyTest { @Mock private ArrayList<Integer> mockList; /** * 注意:这里不能是接口,因为无法初始化并执行 */ @Spy private ArrayList<Integer> spyList; @Test public void spyCallRealMethodTest() { mockList.add(1); mockList.add(2); assertEquals(0, mockList.size()); spyList.add(1); spyList.add(2); assertEquals(2, spyList.size()); } /** * spy方法默认执行,可通过doReturn().when().method()方法让其不执行 */ @Test public void spyDoNotCallRealMethodTest() { doReturn(true).when(spyList).add(1); doReturn(true).when(spyList).add(2); spyList.add(1); spyList.add(2); assertEquals(0, spyList.size()); } }
1.8 断言
Assert、MatcherAssert、CoreMatchers断言
package org.example; import org.example.Domain.User; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Test; import java.util.Arrays; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.everyItem; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.CoreMatchers.startsWith; import org.hamcrest.core.CombinableMatcher; import static org.hamcrest.CoreMatchers.is; public class MatcherAssertTest { @Test public void test1() { User user = new User(1L, "zhangsan", "123456"); Boolean boolen = CoreMatchers.any(User.class).matches(user); Assert.assertTrue(boolen); } @Test public void test2() { User user = new User(1L, "zhangsan", "123456"); Assert.assertThat(user, CoreMatchers.any(User.class)); } // JUnit Matchers assertThat @Test public void testAssertThatBothContainsString() { assertThat("albumen", CoreMatchers.both(containsString("a")).and(containsString("b"))); } @Test public void testAssertThatHasItems() { assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three")); } @Test public void testAssertThatEveryItemContainsString() { assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }), everyItem(containsString("n"))); } // Core Hamcrest Matchers with assertThat @Test public void testAssertThatHamcrestCoreMatchers() { assertThat("good", allOf(equalTo("good"), startsWith("good"))); assertThat("good", not(allOf(equalTo("bad"), equalTo("good")))); assertThat("good", anyOf(equalTo("bad"), equalTo("good"))); assertThat(7, not(CombinableMatcher.<Integer>either(equalTo(3)).or(equalTo(4)))); assertThat(new Object(), not(sameInstance(new Object()))); } @Test public void testHamcrest() throws Exception { assertThat(3, is(3)); assertThat(3, is(not(4))); } }
2 语法
2.1 如何创建mock对象
- Mockito.mock(Class)方法创建
- 通过@Mock注解创建,@RunWith(MockitoJUnitRunner.class)方式激活,@InjectMocks方式注入
2.2 如何打桩(stubbing)
需求 | 非void方法 | void方法 |
语法 | when(obj.method()).thenReturn(xxx); | doNothing().when(obj).method(); |
返回值 | thenReturn(); | |
抛异常 | thenThrow() | doThrow() |
复杂返回逻辑 | thenAnswer() | |
执行原方法 | thenCallRealMethod() | doCallRealMethod() |
断言 | assertEquals(expected,actual) | verify(obj,times(1)).method() |
3.3 @Spy和@Mock区别
特征 | @Mock | @Spy |
执行真实方法 | thenCallRealMethod() | 默认执行 |
不执行真实方法 | 默认不执行 | doReturn().when() |
是否能定义接口 | 是 | 否,因为spy方法需要执行,因此必须是个可实例化的类,不能是接口 |