在前面的文章中(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. .net的内置对象

    一 . 获取客户端,服务器端信息: Response.Write("客户端信息:"); Response.Write("<br>浏览器类型,版本:" ...

  2. swift 官方文档

    swift 官方文档 https://swift.org/blog/

  3. 简述Markdown的使用方法

    MarkdownPad Document Markdown的使用技巧 一.标题 一个”#“表示H1.“##”表示H2... 二.列表 第一点 第二点 注意1.2. -与文本之间要有一个空格 这一点 三 ...

  4. iOS 7.0.2 的bug记录

    在iOS 7.0.2 版本上,如果从主屏幕进入webapp且webapp进入全屏模式,那么alert和修改window.location到某产品对应的itunes下载页面则无效. 可参考下面的代码示例 ...

  5. Boost 库uuid 的使用

    UUID 简介 通用唯一识别码(英语:Universally Unique Identifier,简称UUID)是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分. uuid 版 ...

  6. Android 获取ROOT权限原理解析

    一. 概述 本文介绍了android中获取root权限的方法以及原理,让大家对android玩家中常说的“越狱”有一个更深层次的认识. 二. Root的介绍 1.       Root 的目的 可以让 ...

  7. PHP+Gtk实例(求24点)

    作者: Laruence(   ) 本文地址: http://www.laruence.com/2009/05/26/871.html 转载请注明出处 最近要安排我为BIT提供的<PHP高级应用 ...

  8. 求N的阶乘N!中末尾0的个数

    求N的阶乘N!中末尾0的个数 有道问题是这样的:给定一个正整数N,那么N的阶乘N!末尾中有多少个0呢?例如:N=10,N=3628800,则N!的末尾有两个0:直接上干货,算法思想如下:对于任意一个正 ...

  9. 移动端web及app设计尺寸

    转载 2017年07月27日 22:48:16 984 移动端高清.多屏适配方案 背景 开发移动端H5页面 面对不同分辨率的手机 面对不同屏幕尺寸的手机 视觉稿 在前端开发之前,视觉MM会给我们一个p ...

  10. Hadoop 系列(二)安装配置

    Hadoop 系列(二)安装配置 Hadoop 官网:http://hadoop.apache.or 一.Hadoop 安装 1.1 Hadoop 依赖的组件 JDK :从 Oracle 官网下载,设 ...