Spring Boot 2 实践记录之 封装依赖及尽可能不创建静态方法以避免在 Service 和 Controller 的单元测试中使用 Powermock
在前面的文章中(Spring Boot 2 实践记录之 Powermock 和 SpringBootTest)提到了使用 Powermock 结合 SpringBootTest、WebMvcTest 来 Mock Service、Controller 中的 静态类和静态方法。
但此法有两个弊端,一是这样的单元测试运行速度慢,二是时不时会出现测试运行停顿的情况。
一个可选的方案就是将这些用在 Service、Controller 中的静态类和静态方法的引用,封装在普通 Bean 中,Service、Controller 使用这些 Bean 来完成相应的功能。这样一来,针对 Service、Controller 的单元测试中,就可以使用 @MockBean 结合 Mockito 直接 Mock 这些封装类及其方法了。
例如,在用户注册中,使用了 UUID 类来生成 用户id,使用 Date 插入注册时间,代码如下:
@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; } }
}
对应的单元测试:
@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的示例如下:
工具类:
@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());
...... }