在前面的文章中(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. Python3使用csv模块csv.writer().writerow()保存csv文件,产生空行的问题

    问题:csv.writer().writerow()保存的csv文件,打开时每行后都多一行空行 解决方法:在open()内增加一个参数newline='' 即可   问题现象:   1.代码 with ...

  2. 121. Best Time to Buy and Sell Stock (Array;DP)

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  3. SVN概述

    ----------------------siwuxie095                                 SVN 概述         1.SVN 即 Subversion 的 ...

  4. Redis阅读目录

    一.Redis简介 点击链接查看:https://www.cnblogs.com/hwlong/p/9325986.html 二.Redis安装及基本配置 点击链接查看:https://www.cnb ...

  5. 20-java 对象链表空没空呢

    写了一个 对象链表,往里面add了一些对象,最后我想看下链表是否为空,用  == null  为假,也看不出, 看下长度? 好吧, size() = 1: 打印  null ,  那到底是不是空 啊, ...

  6. mockito使用

    mockito学习资料: http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html http://blog.csdn.net/sdy ...

  7. Linq select 语法

    文档:https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b 1.可以对查询出来的结果做一些转换,下面的例子在数组中查找以"B&q ...

  8. 3D文件压缩库——Draco简析

    3D文件压缩库——Draco简析 今年1月份时,google发布了名为“Draco”的3D图形开源压缩库,下载了其代码来看了下,感觉虽然暂时用不到,但还是有前途的,故简单做下分析. 注:Draco 代 ...

  9. aspx导出文件

    System.IO.StringWriter sw = new System.IO.StringWriter(); HtmlTextWriter htw = new HtmlTextWriter(sw ...

  10. 主频3.0 1g内存是什么意思

    我会讲解一些常用的计算机应用知识.希望大家多多支持,稍后更新,我的技术水平在国内属于顶尖的水平,不服来战...稍后更新...