在前面的文章中(Spring Boot 2 實踐記錄之 Powermock 和 SpringBootTest)提到了使用 Powermock 結合 SpringBootTest、WebMvcTest 來 Mock Service、Controller 中的 靜態類和靜態方法。html
但此法有兩個弊端,一是這樣的單元測試運行速度慢,二是時不時會出現測試運行停頓的狀況。java
一個可選的方案就是將這些用在 Service、Controller 中的靜態類和靜態方法的引用,封裝在普通 Bean 中,Service、Controller 使用這些 Bean 來完成相應的功能。這樣一來,針對 Service、Controller 的單元測試中,就可使用 @MockBean 結合 Mockito 直接 Mock 這些封裝類及其方法了。app
例如,在用戶註冊中,使用了 UUID 類來生成 用戶id,使用 Date 插入註冊時間,代碼以下:less
@Service public class UsersServiceImpl implements UsersServiceInterface { @Autowired private Users users; @Autowired private UsersMapper usersMapper; /** * 用戶註冊 service 方法 * @param userIn * @return */ @Override public String signUp(UserIn userIn) { String result; Date now = new Date(); users.setUserId(UUID.randomUUID().toString()); users.setRegTime(now); ...... try { Integer result = usersMapper.insert(users); if (0 == result) { result = "fail"; } else { result = "success"; } catch (Exception e) { log.error(e.getLocalizedMessage(), e.fillInStackTrace()); result = "fail"; } finally { return result; } }
}
對應的單元測試:dom
@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"}) @PrepareForTest({UsersService.class, Date.class, UUID.class}) @SpringBootTest @Transactional public class UsersServiceMybatisImplTest { @Autowired private UsersServiceMybatisImpl usersServiceMybatis; @MockBean private EncryptInterface encryptInterface; @MockBean private DateUtils dateUtils; @Autowired private UsersMapper usersMapper; private UserIn userIn; @Before public void setUp() throws Exception { userIn = new UserIn(); Mockito.when(encryptInterface.passwordGenerator("hello123")).thenReturn("abcdefghijklmn"); } @After public void tearDown() throws Exception { } @Test public void signUp(){ Date date = (new GregorianCalendar(2018, 11, 9)).getTime(); PowerMockito.mockStatic(Date.class); PowerMockito.whenNew(Date.class).withNoArgments().thenReturn(date); String randomString = "abcdefg"; UUID uuid = PowerMockito.mock(UUID.class); Mockito.when(uuid.toString()).thenReturn(randomString); PowerMockito.mockStatic(UUID.class); PowerMockito.when(UUID.randomUUID()).thenReturn(uuid); userIn.setUserId("admin123"); userIn.setSign("盤古氏"); userIn.setEmail("pangu@gushen.com"); userIn.setPassword("hello123"); userIn.setRePassword("hello123"); userIn.setIp("127.0.0.1"); userIn.setNick("盤古"); userIn.setSchool("混沌大學"); assertEquals("success", result);
Users user = usersMapper.selectByPrimaryKey("abcdefg"); assertEquals(date.toString(), user.getRegTime().toString());
......
} }
將靜態類和靜態方法封裝成普通 Bean的示例以下:ide
工具類:工具
@Component public class DateUtils { public Date generateDate() { return new Date(); } }
@Component public class UUIDUtils { public Date generateUUID() { return UUID.randomUUID(); } }
Service 類:單元測試
@Service public class UsersServiceImpl implements UsersServiceInterface { @Autowired private Users users; @Autowired private UsersMapper usersMapper; @Autowired private DateUtils dateUtils; @Autowired private UUIDUtils uuidUtils; /** * 用戶註冊 service 方法 * @param userIn * @return */ @Override public String signUp(UserIn userIn) { String result; Date now = dateUtils.generateDate(); users.setUserId(uuidUtils.generateUUID().toString()); users.setRegTime(now); ...... try { Integer result = usersMapper.insert(users); if (0 == result) { result = "fail"; } else { result = "success"; } catch (Exception e) { log.error(e.getLocalizedMessage(), e.fillInStackTrace()); result = "fail"; } finally { return result; } } }
單元測試:測試
@RunWith(SpringRunner.class) @SpringBootTest @Transactional public class UsersServiceMybatisImplTest { @Autowired private UsersServiceMybatisImpl usersServiceMybatis; @MockBean private EncryptInterface encryptInterface; @MockBean private DateUtils dateUtils; @MockBean private UUIDUtils uuidUtils; @Autowired private UsersMapper usersMapper; private UserIn userIn; @Before public void setUp() throws Exception { userIn = new UserIn(); Mockito.when(encryptInterface.passwordGenerator("hello123")).thenReturn("abcdefghijklmn"); } @After public void tearDown() throws Exception { } @Test public void signUp(){ Date date = (new GregorianCalendar(2018, 11, 9)).getTime(); Mockito.when(dateUtils.generateDate()).thenReturn(date); UUID uuid = Mockito.mock(UUID.randomUUID()); Mockito.when(uuid.toString()).thenReturn("abcdefg"); Mockito.when(uuidUtils.generateUUID()).thenReturn(uuid); userIn.setUserId("admin123"); userIn.setSign("盤古氏"); userIn.setEmail("pangu@gushen.com"); userIn.setPassword("hello123"); userIn.setRePassword("hello123"); userIn.setIp("127.0.0.1"); userIn.setNick("盤古"); userIn.setSchool("混沌大學"); assertEquals("success", result); Users user = usersMapper.selectByPrimaryKey("abcdefg"); assertEquals(date.toString(), user.getRegTime().toString()); ...... }