在前面的文章中(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());
......
}

Spring Boot 2 实践记录之 封装依赖及尽可能不创建静态方法以避免在 Service 和 Controller 的单元测试中使用 Powermock的更多相关文章

  1. Spring Boot 2 实践记录之 使用 ConfigurationProperties 注解将配置属性匹配至配置类的属性

    在 Spring Boot 2 实践记录之 条件装配 一文中,曾经使用 Condition 类的 ConditionContext 参数获取了配置文件中的配置属性.但那是因为 Spring 提供了将上 ...

  2. Spring Boot 2 实践记录之 MyBatis 集成的启动时警告信息问题

    按笔者 Spring Boot 2 实践记录之 MySQL + MyBatis 配置 中的方式,如果想正确运行,需要在 Mapper 类上添加 @Mapper 注解. 但是加入此注解之后,启动时会出现 ...

  3. Spring Boot 2 实践记录之 Redis 及 Session Redis 配置

    先说 Redis 的配置,在一些网上资料中,Spring Boot 的 Redis 除了添加依赖外,还要使用 XML 或 Java 配置文件做些配置,不过经过实践并不需要. 先在 pom 文件中添加 ...

  4. Spring Boot 2 实践记录之 Powermock 和 SpringBootTest

    由于要代码中使用了 Date 类生成实时时间,单元测试中需要 Mock Date 的构造方法,以预设其行为,这就要使用到 PowerMock 在 Spring Boot 的测试套件中,需要添加 @Ru ...

  5. Spring Boot 2 实践记录之 使用 Powermock、Mockito 对 UUID 进行 mock 单元测试

    由于注册时,需要对输入的密码进行加密,使用到了 UUID.sha1.md 等算法.在单元测试时,使用到了 Powermock,记录如下. 先看下加密算法: import org.apache.comm ...

  6. Spring Boot 2 实践记录之 MySQL + MyBatis 配置

    如果不需要连接池,那么只需要简单的在pom文件中,添加mysql依赖: <dependency> <groupId>mysql</groupId> <arti ...

  7. Spring Boot 2 实践记录之 组合注解原理

    Spring 的组合注解功能,网上有很多文章介绍,不过都是介绍其使用方法,鲜有其原理解析. 组合注解并非 Java 的原生能力.就是说,想通过用「注解A」来注解「注解B」,再用「注解B」 来注解 C( ...

  8. Spring Boot 2 实践记录之 条件装配

    实验项目是想要使用多种数据库访问方式,比如 JPA 和 MyBatis. 项目的 Service 层业务逻辑相同,只是具体实现代码不同,自然是一组接口,两组实现类的架构比较合理. 不过这种模式却有一个 ...

  9. Spring Boot 之日志记录

    Spring Boot 之日志记录 Spring Boot 支持集成 Java 世界主流的日志库. 如果对于 Java 日志库不熟悉,可以参考:细说 Java 主流日志工具库 关键词: log4j, ...

随机推荐

  1. 第八章 高级搜索树 (b4)B-树: 插入

  2. python版本安装

    目的 本文目的在于,对于不熟悉Python的人,教你: 1. 从哪里找到 可以下载到 各种版本的 包括Python 2.x和Python 3.x的 最新版本的 Python. 高手请无视之. 2.以及 ...

  3. Future设计模式

    一.什么是Future模型: Future模式是多线程开发中非常常见的一种设计模式,它的核心思想是异步调用.这类似我们网上订餐订座,只要一个电话,客服就告诉我们已经预定成功(实际客服MM啥都还没做好) ...

  4. vue小常识小注意

    1,跨域携带cookie,加一个拦截器,能后加上request.credentials=true就可以了 Vue.http.interceptors.push(function(request, ne ...

  5. Oracle增加一列、修改一列数据类型

    Oracle增加一列.修改一列数据类型: 添加一列: alter   table   A   add( CFYJSNR  varchar2(20)); 修改列: alter  table A  ren ...

  6. Mathtype使用技巧

    1. 打开/关闭MathType窗口 Alt+Ctrl+q:插入inline公式   Ctrl+S:更新公式到Word相应位置 Alt+F4:保存并关闭MathType窗口,返回Word. 2. 公式 ...

  7. Codeforces 689B. Mike and Shortcuts SPFA/搜索

    B. Mike and Shortcuts time limit per test: 3 seconds memory limit per test: 256 megabytes input: sta ...

  8. [BAT]批处理脚本双击可运行,但在定时计划任务中无法执行(当前工作路径不对导致的)

    一开始,红色部分我是用set AutoPath=%cd%,双击可执行,但是将这个批处理脚本放在定时任务中无法执行,后来发现在定时执行的时候,当前工作路径不是批处理脚本所在的路径,而是C:/Window ...

  9. 彻底关闭win10的自动更新,一定要坚持看到最后一页

    https://jingyan.baidu.com/article/9faa7231e7b78b473c28cbb6.html 是否好使我还在测试,不好使的话,再跟新 2018-05-20 今天电脑又 ...

  10. PacBio下机数据如何看?

    一开始拿到三代测序的下机数据时,蒙了,readme ?三代测序的下机数据都有哪些,以及他们具体的格式是怎么样的(以sequel 平台为主). 测序过程 SMRTbell A adapter通用接头,两 ...