SpringBoot | 第十三章:测试相关(单元测试、性能测试)
前言
前面写了这么多章节,都是通过浏览器访问的形式,进行接口方法访问进而验证方法的正确与否。显然在服务或者接口比较少时,这么做没有啥问题,但一旦一个项目稍微复杂或者接口方法比较多时,这么验证就有点不符合程序猿的
懒人
的特性了。所以这章节,讲述下SpringBoot
中的单元测试及基于Contiperf
压测工具进行性能测试相关方面的知识点。
- 单元测试
- SpringBoot的单元测试
- [RESTful API 单元测试](#RESTful API 单元测试)
- Junit常用注解说明
- 基于ContiPerf的性能测试
- 总结
- 最后
- 老生常谈
单元测试
是指对软件中的最小可
测试单元
进行检查和验证。一般上在开发阶段或者程序发布时,都会利用像Maven
这样的打包工具进行打包前的测试,避免不必要的bug程序被打包部署。
题外话:在开发阶段,都应该要求编写单元测试,核心的模块还需要进行覆盖测试,覆盖率至少要95%以上。
SpringBoot的单元测试
对于
java
开发者而言,Junit
应该无人不知了。所以SpringBoot
也是基于Junit
进行单位测试的。
0.加入pom依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
1.这里为了演示,编写了一个简单的测试接口及编写对应的测试类。
UnitTestService.java
/**
* 测试接口类
* @author oKong
*
*/
public interface UnitTestService {
public String process(String msg);
}
实现类:UnitTestServiceImpl.java
@Service
public class UnitTestServiceImpl implements UnitTestService{
/**
* 为了测试,这里直接返回传入的值
*/
@Override
public String process(String msg) {
// TODO Auto-generated method stub
return msg;
}
}
测试类:UnitTestServiceTest.java
题外话:个人建议,每个测试类都应该和对应的被测试类包路径一致。同时测试类的名称是被测试的类名+Test,如本例所示的:
/**
* 编写接口测试类
* @author oKong
*
*/
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
@SpringBootTest
public class UnitTestServiceTest {
@Autowired
UnitTestService testService;
public void test() {
String msg = "this is a test";
String result = testService.process(msg);
//断言 是否和预期一致
Assert.assertEquals(msg, result);
}
}
- 运行右击,选择
run As
-->Junit Test
或者需要debug时,选择Debug As
-->Junit Test
,运行即可。
3.至此,一个简单的单元测试就结束了。简单来说,写一个单元测试是容易的,但写好一个单元测试是难的。毕竟,每个程序猿都觉得自己的代码是没有问题的,难道不是吗?哈哈!
RESTful API 单元测试
对于服务类而言,编写单元测试是相对简单的,只需要像控制层自动引入接口类一样。但编写控制层即RESTful API 单元测试时,一般上就需要利用
Mock
技术进行测试了。当然也可以使用像Swagger
或者PostMan
这样的api测试工具进行测试(或者使用RestTemplate
测试也是可行的),它可进行自动化测试,关于Postman
会在之后的章节进行更新,作者也没有过多研究过,也只是用到了它的最基本的发起http请求的功能,之后会整理相关资料的。
0.创建一个RESTful接口服务。
/**
* 编写mock测试服务
* @author oKong
*
*/
@RestController
public class DemoController {
@GetMapping("/mock")
public String demo(String msg) {
return msg;
}
}
1.编写对应测试类
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
//因为是mock测试,在实际开发过程中,可指定其测试启动时为随机端口,避免了不必要的端口冲突。
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
//测试单一接口时 ,也可利用注解@WebMvcTest 进行单一测试
//@WebMvcTest(DemoController.class)
public class DemoControllerTest {
//使用 WebMvcTest 时使用
//@autowired mockMvc 是可自动注入的。
//当直接使用SpringBootTest 会提示 注入失败 这里直接示例利用 MockMvcBuilders工具创建
//@Autowired
MockMvc mockMvc;
@Autowired
WebApplicationContext wc;
@Before
public void beforeSetUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wc).build();
}
@Test
public void testDemo() throws Exception {
String msg = "this is a mock test";
MvcResult result = this.mockMvc.perform(get("/mock").param("msg", msg)).andDo(print()).andExpect(status().isOk())
.andReturn();
//断言 是否和预期相等
Assert.assertEquals(msg, result.getResponse().getContentAsString());
}
}
2.运行右击,选择 run As
--> Junit Test
或者需要debug时,选择Debug As
--> Junit Test
,运行即可。(也可以看见每次启动测试时,每次端口号都是不同的。)
2018-07-25 23:16:28.733 INFO 13000 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 59999 (http)
2018-07-25 23:16:28.754 INFO 13000 --- [ main] c.l.l.s.c.controller.DemoControllerTest : Started DemoControllerTest in 5.673 seconds (JVM running for 6.769)
由于配置了print()
这个ResultHandler
,所以控制台会打印相关参数信息。建议设置此属性,这样就算测试有问题,也能看下具体的参数信息。其他相关mock的用法,此处就不举例了,大家可自行搜索下,比较本章节只是简单示例下用法~
- 鉴于每次编写控制层测试类时,都需要创建
MockMvc
对象,故可创建一个基类,这样省得每次都写。
BaseMockTest.java
/**
* mock 基类
* @author oKong
*
*/
public abstract class BaseMockTest {
@Autowired
private WebApplicationContext wc;
protected MockMvc mockMvc;
@Before
public void beforeSetUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wc).build();
}
}
这样编写mock测试类时,还需要继承此基类即可。
Junit常用注解说明
- @Test 加在待测试的方法前面
- @Before 带上@Test的方法执行前会执行该方法
- @After 带上@Test的方法执行完毕后会执行该方法
- @BeforeClass 加上这个注解,则该方法会第一个执行(在所有方法中),且方法要加上关键词static,是一个static方法
- @AfterClass 加上这个注解,则该方法最后一个执行(在所有方法中),同样,方法要加上关键词static,是一个static方法
详细的使用,大家可自行谷歌下,毕竟常用的也就前面三个了,(┬_┬)
基于ContiPerf的性能测试
ContiPerf是一个轻量级的测试工具,基于JUnit 4 开发,可用于效率测试等。可以指定在线程数量和执行次数,通过限制最大时间和平均执行时间来进行性能测试。
性能测试示例
0.加入pom依赖包。
<dependency>
<groupId>org.databene</groupId>
<artifactId>contiperf</artifactId>
<version>2.3.4</version>
<scope>test</scope>
</dependency>
1.改写UnitTestServiceTest
测试类,进入ContiPerfRule
。
题外话:@Rule
是Junit
提供的一个扩展接口注解,其接口类为:org.junit.rules.MethodRule
,注意在Junit5中,已经被TestRule
所以替代了。这里只是简单提下,因为具体的也不是很清楚,也没有深入了解过。
/**
* 编写接口测试类
* @author oKong
*
*/
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
@SpringBootTest
public class UnitTestServiceTest {
@Autowired
UnitTestService testService;
//引入 ContiPerf 进行性能测试
@Rule
public ContiPerfRule contiPerfRule = new ContiPerfRule();
@Test
//10个线程 执行10次
@PerfTest(invocations = 100,threads = 10)
public void test() {
String msg = "this is a test";
String result = testService.process(msg);
//断言 是否和预期一致
Assert.assertEquals(msg, result);
}
}
- 控制台会有性能报告,同时访问:
target/contiperf-report/index.html
,会有图表提示。
控制台输出:
cn.lqdev.learning.springboot.chapter13.service.UnitTestServiceTest.test
samples: 100
max: 403
average: 41.5
median: 15
测试报告:
注解参数说明
@PerfTest
- invocations:执行次数n,与线程数量无关,默认值为1
- threads:线程池数量n,并发执行n个线程
- duration:重复地执行时间n,测试至少执行n毫秒
@Required
- throughput:吞吐要求n,要求每秒至少执行n个测试
- average:平均执行时间n,要求平均执行时间不超过n毫秒
- max:最大执行时间n,要求最大的执行时间不超过n毫秒
- totalTime:总执行时间n,要求总的执行时间不超过n毫秒
- median:50%平均执行时间n,要求所有执行的50%测试平均执行时间不超过n毫秒
- percentile90:90%平均执行时间n,要求所有执行的90%测试平均执行时间不超过n毫秒
- percentile95:95%平均执行时间n,要求所有执行的95%测试平均执行时间不超过n毫秒
- percentile99:99%平均执行时间n,要求所有执行的99%测试平均执行时间不超过n毫秒
- percentiles:表达式"a:n,b:m",要求a%的测试不超过n毫秒,b%的测试不超过m毫秒
总结
本章节主要是对
Junit
和ContiPerf
的使用简单的示例,像MockMvc
的详细用法并没有深入,大家可自行搜索下,毕竟我也用的不多呀。
最后
目前互联网上很多大佬都有
SpringBoot
系列教程,如有雷同,请多多包涵了。本文是作者在电脑前一字一句敲的,每一步都是实践的。若文中有所错误之处,还望提出,谢谢。
老生常谈
- 个人QQ:
499452441
- 微信公众号:
lqdevOps
完整示例:chapter-13
原文地址:http://blog.lqdev.cn/2018/07/26/springboot/chapter-thirteen/
SpringBoot | 第十三章:测试相关(单元测试、性能测试)的更多相关文章
- SpringBoot | 第二十三章:日志管理之整合篇
前言 在本系列<第四章:日志管理>中,由于工作中日志这块都是走默认配置,也没有深入了解过,因为部署过程中直接使用了linux中的输出重定向功能,如java -jar xx.jar > ...
- IO流入门-第十三章-File相关
/* java.io.File 1.File和流无关,不能通过该类完成文件的读写 2.File是文件和目录路径名的抽象变现形式. */ import java.io.*; public class F ...
- SpringBoot | 第零章:前言
缘起 前段时间公司领导叫编写一两课关于springboot的基础知识培训课程,说实话,也是今年年初才开始接触了SpringBoot这个脚手架,使用了之后才发现打开了一个新世界.再之后也没有一些系统的学 ...
- Gradle 1.12用户指南翻译——第二十三章. Java 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- 《汇编语言 基于x86处理器》第十三章高级语言接口部分的代码 part 2
▶ 书中第十三章的程序,主要讲了汇编语言和 C/++ 相互调用的方法 ● 代码,汇编中调用 C++ 函数 ; subr.asm INCLUDE Irvine32.inc askForInteger P ...
- CentOS7安装CDH 第十三章:CDH资源池配置
相关文章链接 CentOS7安装CDH 第一章:CentOS7系统安装 CentOS7安装CDH 第二章:CentOS7各个软件安装和启动 CentOS7安装CDH 第三章:CDH中的问题和解决方法 ...
- 鸟哥的linux私房菜——第十三章学习(Linux 帐号管理与 ACLL 权限设置)
第十三章.Linux 帐号管理与 ACLL 权限设置 1.0).使用者识别码: UID 与 GID UID :User ID GID :group ID [root@study ~]# ll -d / ...
- PRML读书会第十三章 Sequential Data(Hidden Markov Models,HMM)
主讲人 张巍 (新浪微博: @张巍_ISCAS) 软件所-张巍<zh3f@qq.com> 19:01:27 我们开始吧,十三章是关于序列数据,现实中很多数据是有前后关系的,例如语音或者DN ...
- [汇编学习笔记][第十三章int指令]
第十三章int指令 13.1 int指令 格式: int n, n 为中断类型码 可以用int指令调用任何一个中断的中断处理程序(简称中断例程). 13.4 BIOS和DOS 所提供的中断例程 BIO ...
随机推荐
- k8s 基础 核心概念
Pod pod是若干相关容器的集合,Pod包含的容器运行在同一台宿主机上,这些容器使用相同的网络命名空间,ip地址和端口,相互之间能通过localhost来发现和通信.另外,这些容器还可共享一块存储空 ...
- web安全之XSS和CSRF
XSS 跨站脚本攻击(cross site script),本来缩写CSS单位了和层叠样式(Cascading Style Sheet,CSS)有所区别,所以在安全领域叫做“XSS”. XSS攻击,通 ...
- MS SQL update set select
有张表a,已经有数据 再有张表b,也已查询出数据 两张表有外键关联 需求如下: 更新表a中的某个字段,这个字段要加上(都是int型的数据)对应表b中的数据作为更新的最终数据 )) from #libL ...
- Material使用04 MdCardModule和MdButtonModule综合运用
设计需求:设计一个登陆页面 1 模块导入 1.1 将MdCardModule和MdButtonModule模块导入到共享模块中 import { NgModule } from '@angular/c ...
- Shrio00 Shiro认证登录、权限管理环境搭建
基础环境准备: JDK -> java version "1.8.0_101" MAVEN -> Apache Maven 3.5.0 1 导入依赖 mysql驱动 m ...
- 3. 从零开始学CSRF
为什么要拿CSRF来当“攻击手法系列”的开头篇呢?因为CSRF/XSRF我个人喜爱他的程度已经超过XSS了.如果说XSS是一个老虎,那么CSRF就是隐藏在暗处的蛇. 相信现在很多人不明 ...
- vue中v-if 与v-show的区别
v-if vs v-show v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建. v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做—— ...
- 浅谈JavaScript--事件委托与事件监听
事件监听 该方法用于向指定元素添加事件句柄(代码块),且不会覆盖已存在的事件句柄. 即可以向同一个元素添加同一个事件多次. 添加事件 语法: element.addEventListener(even ...
- AIM Tech Round 5 (rated, Div. 1 + Div. 2) D(SET,思维)
#include<bits/stdc++.h>using namespace std;const long long mod = 1e9+7;char s[370007][27];long ...
- python文件名匹配
待匹配文件:#FY3D_IPMNT_GBAL_L1_20180516_0003_030KM_MS.HDF 干扰文件:#FY3D_IPMNT_GBAL_L1_20180516_0003_030KM_MS ...