前言

前面写了这么多章节,都是通过浏览器访问的形式,进行接口方法访问进而验证方法的正确与否。显然在服务或者接口比较少时,这么做没有啥问题,但一旦一个项目稍微复杂或者接口方法比较多时,这么验证就有点不符合程序猿的懒人的特性了。所以这章节,讲述下SpringBoot中的单元测试及基于Contiperf压测工具进行性能测试相关方面的知识点。

单元测试

是指对软件中的最小可测试单元进行检查和验证。一般上在开发阶段或者程序发布时,都会利用像Maven这样的打包工具进行打包前的测试,避免不必要的bug程序被打包部署。

题外话:在开发阶段,都应该要求编写单元测试,核心的模块还需要进行覆盖测试,覆盖率至少要95%以上。

SpringBoot的单元测试

对于java开发者而言,Junit应该无人不知了。所以SpringBoot也是基于Junit进行单位测试的。

0.加入pom依赖。

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-test</artifactId>
  4. <scope>test</scope>
  5. </dependency>

1.这里为了演示,编写了一个简单的测试接口及编写对应的测试类。

UnitTestService.java

  1. /**
  2. * 测试接口类
  3. * @author oKong
  4. *
  5. */
  6. public interface UnitTestService {
  7. public String process(String msg);
  8. }

实现类:UnitTestServiceImpl.java

  1. @Service
  2. public class UnitTestServiceImpl implements UnitTestService{
  3. /**
  4. * 为了测试,这里直接返回传入的值
  5. */
  6. @Override
  7. public String process(String msg) {
  8. // TODO Auto-generated method stub
  9. return msg;
  10. }
  11. }

测试类:UnitTestServiceTest.java

题外话:个人建议,每个测试类都应该和对应的被测试类包路径一致。同时测试类的名称是被测试的类名+Test,如本例所示的:

  1. /**
  2. * 编写接口测试类
  3. * @author oKong
  4. *
  5. */
  6. @RunWith(SpringRunner.class)
  7. //SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
  8. @SpringBootTest
  9. public class UnitTestServiceTest {
  10. @Autowired
  11. UnitTestService testService;
  12. public void test() {
  13. String msg = "this is a test";
  14. String result = testService.process(msg);
  15. //断言 是否和预期一致
  16. Assert.assertEquals(msg, result);
  17. }
  18. }
  1. 运行右击,选择 run As --> Junit Test 或者需要debug时,选择Debug As --> Junit Test,运行即可。

3.至此,一个简单的单元测试就结束了。简单来说,写一个单元测试是容易的,但写好一个单元测试是难的。毕竟,每个程序猿都觉得自己的代码是没有问题的,难道不是吗?哈哈!

RESTful API 单元测试

对于服务类而言,编写单元测试是相对简单的,只需要像控制层自动引入接口类一样。但编写控制层即RESTful API 单元测试时,一般上就需要利用Mock技术进行测试了。当然也可以使用像Swagger或者PostMan这样的api测试工具进行测试(或者使用RestTemplate测试也是可行的),它可进行自动化测试,关于Postman会在之后的章节进行更新,作者也没有过多研究过,也只是用到了它的最基本的发起http请求的功能,之后会整理相关资料的。

0.创建一个RESTful接口服务。

  1. /**
  2. * 编写mock测试服务
  3. * @author oKong
  4. *
  5. */
  6. @RestController
  7. public class DemoController {
  8. @GetMapping("/mock")
  9. public String demo(String msg) {
  10. return msg;
  11. }
  12. }

1.编写对应测试类

  1. @RunWith(SpringRunner.class)
  2. //SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
  3. //因为是mock测试,在实际开发过程中,可指定其测试启动时为随机端口,避免了不必要的端口冲突。
  4. @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
  5. //测试单一接口时 ,也可利用注解@WebMvcTest 进行单一测试
  6. //@WebMvcTest(DemoController.class)
  7. public class DemoControllerTest {
  8. //使用 WebMvcTest 时使用
  9. //@autowired mockMvc 是可自动注入的。
  10. //当直接使用SpringBootTest 会提示 注入失败 这里直接示例利用 MockMvcBuilders工具创建
  11. //@Autowired
  12. MockMvc mockMvc;
  13. @Autowired
  14. WebApplicationContext wc;
  15. @Before
  16. public void beforeSetUp() {
  17. this.mockMvc = MockMvcBuilders.webAppContextSetup(wc).build();
  18. }
  19. @Test
  20. public void testDemo() throws Exception {
  21. String msg = "this is a mock test";
  22. MvcResult result = this.mockMvc.perform(get("/mock").param("msg", msg)).andDo(print()).andExpect(status().isOk())
  23. .andReturn();
  24. //断言 是否和预期相等
  25. Assert.assertEquals(msg, result.getResponse().getContentAsString());
  26. }
  27. }

2.运行右击,选择 run As --> Junit Test 或者需要debug时,选择Debug As --> Junit Test,运行即可。(也可以看见每次启动测试时,每次端口号都是不同的。)

  1. 2018-07-25 23:16:28.733 INFO 13000 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 59999 (http)
  2. 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的用法,此处就不举例了,大家可自行搜索下,比较本章节只是简单示例下用法~

  1. 鉴于每次编写控制层测试类时,都需要创建MockMvc对象,故可创建一个基类,这样省得每次都写。

BaseMockTest.java


  1. /**
  2. * mock 基类
  3. * @author oKong
  4. *
  5. */
  6. public abstract class BaseMockTest {
  7. @Autowired
  8. private WebApplicationContext wc;
  9. protected MockMvc mockMvc;
  10. @Before
  11. public void beforeSetUp() {
  12. this.mockMvc = MockMvcBuilders.webAppContextSetup(wc).build();
  13. }
  14. }

这样编写mock测试类时,还需要继承此基类即可。

Junit常用注解说明

  • @Test 加在待测试的方法前面
  • @Before 带上@Test的方法执行前会执行该方法
  • @After 带上@Test的方法执行完毕后会执行该方法
  • @BeforeClass 加上这个注解,则该方法会第一个执行(在所有方法中),且方法要加上关键词static,是一个static方法
  • @AfterClass 加上这个注解,则该方法最后一个执行(在所有方法中),同样,方法要加上关键词static,是一个static方法

详细的使用,大家可自行谷歌下,毕竟常用的也就前面三个了,(┬_┬)

基于ContiPerf的性能测试

ContiPerf是一个轻量级的测试工具,基于JUnit 4 开发,可用于效率测试等。可以指定在线程数量和执行次数,通过限制最大时间和平均执行时间来进行性能测试。

性能测试示例

0.加入pom依赖包。

  1. <dependency>
  2. <groupId>org.databene</groupId>
  3. <artifactId>contiperf</artifactId>
  4. <version>2.3.4</version>
  5. <scope>test</scope>
  6. </dependency>

1.改写UnitTestServiceTest测试类,进入ContiPerfRule

题外话:@RuleJunit提供的一个扩展接口注解,其接口类为:org.junit.rules.MethodRule,注意在Junit5中,已经被TestRule所以替代了。这里只是简单提下,因为具体的也不是很清楚,也没有深入了解过。

  1. /**
  2. * 编写接口测试类
  3. * @author oKong
  4. *
  5. */
  6. @RunWith(SpringRunner.class)
  7. //SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
  8. @SpringBootTest
  9. public class UnitTestServiceTest {
  10. @Autowired
  11. UnitTestService testService;
  12. //引入 ContiPerf 进行性能测试
  13. @Rule
  14. public ContiPerfRule contiPerfRule = new ContiPerfRule();
  15. @Test
  16. //10个线程 执行10次
  17. @PerfTest(invocations = 100,threads = 10)
  18. public void test() {
  19. String msg = "this is a test";
  20. String result = testService.process(msg);
  21. //断言 是否和预期一致
  22. Assert.assertEquals(msg, result);
  23. }
  24. }
  1. 控制台会有性能报告,同时访问:target/contiperf-report/index.html,会有图表提示。

控制台输出:

  1. cn.lqdev.learning.springboot.chapter13.service.UnitTestServiceTest.test
  2. samples: 100
  3. max: 403
  4. average: 41.5
  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毫秒

总结

本章节主要是对JunitContiPerf的使用简单的示例,像MockMvc的详细用法并没有深入,大家可自行搜索下,毕竟我也用的不多呀。

最后

目前互联网上很多大佬都有SpringBoot系列教程,如有雷同,请多多包涵了。本文是作者在电脑前一字一句敲的,每一步都是实践的。若文中有所错误之处,还望提出,谢谢。

老生常谈

  • 个人QQ:499452441
  • 微信公众号:lqdevOps

个人博客:https://blog.lqdev.cn

完整示例:chapter-13

原文地址:http://blog.lqdev.cn/2018/07/26/springboot/chapter-thirteen/

SpringBoot | 第十三章:测试相关(单元测试、性能测试)的更多相关文章

  1. SpringBoot | 第二十三章:日志管理之整合篇

    前言 在本系列<第四章:日志管理>中,由于工作中日志这块都是走默认配置,也没有深入了解过,因为部署过程中直接使用了linux中的输出重定向功能,如java -jar xx.jar > ...

  2. IO流入门-第十三章-File相关

    /* java.io.File 1.File和流无关,不能通过该类完成文件的读写 2.File是文件和目录路径名的抽象变现形式. */ import java.io.*; public class F ...

  3. SpringBoot | 第零章:前言

    缘起 前段时间公司领导叫编写一两课关于springboot的基础知识培训课程,说实话,也是今年年初才开始接触了SpringBoot这个脚手架,使用了之后才发现打开了一个新世界.再之后也没有一些系统的学 ...

  4. Gradle 1.12用户指南翻译——第二十三章. Java 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  5. 《汇编语言 基于x86处理器》第十三章高级语言接口部分的代码 part 2

    ▶ 书中第十三章的程序,主要讲了汇编语言和 C/++ 相互调用的方法 ● 代码,汇编中调用 C++ 函数 ; subr.asm INCLUDE Irvine32.inc askForInteger P ...

  6. CentOS7安装CDH 第十三章:CDH资源池配置

    相关文章链接 CentOS7安装CDH 第一章:CentOS7系统安装 CentOS7安装CDH 第二章:CentOS7各个软件安装和启动 CentOS7安装CDH 第三章:CDH中的问题和解决方法 ...

  7. 鸟哥的linux私房菜——第十三章学习(Linux 帐号管理与 ACLL 权限设置)

    第十三章.Linux 帐号管理与 ACLL 权限设置 1.0).使用者识别码: UID 与 GID UID :User ID GID :group ID [root@study ~]# ll -d / ...

  8. PRML读书会第十三章 Sequential Data(Hidden Markov Models,HMM)

    主讲人 张巍 (新浪微博: @张巍_ISCAS) 软件所-张巍<zh3f@qq.com> 19:01:27 我们开始吧,十三章是关于序列数据,现实中很多数据是有前后关系的,例如语音或者DN ...

  9. [汇编学习笔记][第十三章int指令]

    第十三章int指令 13.1 int指令 格式: int n, n 为中断类型码 可以用int指令调用任何一个中断的中断处理程序(简称中断例程). 13.4 BIOS和DOS 所提供的中断例程 BIO ...

随机推荐

  1. js之递归拼树(树结构的数据结构)

  2. PHPstorm 常用快捷键操作

    1.ctrl+ N: 查找类 2.ctrl+ shift+ N: 全局搜索文件 ,优先文件名匹配的文件 3.ctrl + G: 定位行,跳转行 4.ctrl + F12: 显示当前页面类的所有方法 / ...

  3. 集合对象与自定义javabean对象接收数据库查询的数据 (基础知识扫盲)

    一.集合对象(List,Map,数组)等对象接收数据库查询的记录,如果没有一条记录,就得到的内容为空的集合,不是null: 例如:List查不到记录得到的就是size=0的list 二.自定义的jav ...

  4. linux日常管理-系统进程查看工具-ps

    查看系统有那些进程 命令有ps aux 和命令 ps -elf USER  哪个用户使用了这个进程 PID  进程的id %CPU 占用CPU的百分比 %MEM 占用内存的百分比 VSZ 虚拟内存的大 ...

  5. T-SQL 高级编程

    在Sql Server 中访问数据库一般有2种方式: 1.一种是使用应用程序编程接口API 2.数据库语句 变量:局部变量:以@为前缀,如@Age:全局变量以@@为前缀:(Ps:全局变量以系统定义和维 ...

  6. MyBatis构建sql时动态传入表名以及字段名

    今天项目需要用到动态表名,找到这一篇文章,亲测可用 用了mybatis很长一段时间了,但是感觉用的都是比较基本的功能,很多mybatis相对ibatis的新功能都没怎么用过.比如其内置的注解功能之类的 ...

  7. IE和W3c盒模型

    盒子模型是css中一个重要的概念,理解了盒子模型才能更好的排版.其实盒子模型有两种,分别是 ie 盒子模型和标准 w3c 盒子模型.他们对盒子模型的解释各不相同,先来看看我们熟知的标准盒子模型: 从上 ...

  8. JDBC操作MySQL(crud)

    这两天复习了一下JDBC操作MySQL,把crud操作的例子记一下, 类库链接(mysql-connector-java-5.1.37-bin.jar):http://files.cnblogs.co ...

  9. 关于C/C++中的“auto”关键字

    C/C++ 98标准 C++03标准 早在C++98标准中就存在了auto关键字,那时的auto用于声明变量为自动变量,自动变量意为拥有自动的生命期.此用法是多余的,因为即使定义变量时不加" ...

  10. p2341&bzoj1051 受欢迎的牛

    传送门(洛谷) 传送门(bzoj) 题目 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C ...