当前位置: 澳门新濠3559 > 编程 > 正文

都应该要求编写单元测试,并发执行30个线程

时间:2019-10-06 23:44来源:编程
第十三章:测试相关(单元测试、性能测试) ContiPerf 是一个轻量级的测试工具,基于JUnit 4开发,可用于性能测试等。可以指定在线程数量和执行次数,通过限制最大时间和平均执行时

图片 1第十三章:测试相关(单元测试、性能测试)

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

1、Junit4学习2、断言Assert学习

文章摘要:追求代码质量一直都是优秀程序员对自己的目标,那么有什么好方法能够实现这个目标?

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

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

使用步骤:

一、使用IDEA创建单元测试

在需要测试的类右键,在弹出框选择GO TO->test 图片 2图片.png 然后再对应打勾选择测试方法 图片 3图片.png

在每个系统上线正式发布之前,开发同事对其中功能点进行自测,测试同事根据前期设计的测试用例进行功能测试的都是保障系统可靠稳定运行的重要前提。但是,系统上线后故障还偶有发生,那么如何才能将系统代码质量提高一个档次做到上线后0故障的目标呢?我想这个问题一直是许多研发同学和测试同学共同追求的一个目标,但光靠代码review、简单的自测和功能测试用例覆盖还是不够,需要从代码覆盖率(包括语句覆盖率、分支覆盖率和路径覆盖率等)的角度来解决。因此,本文从解决问题的根本原因出发介绍以SpringBoot工程的自动化单元测试用例结合Cobetura插件来实现定时跑测试任务并生成测试报告。

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

  1. maven 引入

二、知识点记录

  1. @Test注解:是Junit的测试方法。注意:测试方法必须是public void来修饰,并且不包含参数
  2. @BeforeClass注解:这个注解表示这个方法会在所有测试方法执行之前执行,因为是static修饰的静态方法,所以只会执行一次。通常用来进行一些资源的加载。
  3. @AfterClass注解:这个注解表示这个方法会在所有测试方法执行之后执行,因为是static修饰的静态方法,所以只会执行一次。通常用来进行一些资源的释放。
  4. @Before注解:这个注解表示这个方法会在每个测试方法之前执行一次,即有多少个测试方法就会执行多少次。
  5. @After注解:这个注解表示这个方法会在每个测试方法之后执行一次,即有多少个测试方法就会执行多少次。
  6. @RunWith注解:可以更改测试运行器org.junit.runner.Runner。通常用来组织多个测试类一起运行,称为测试套件,测试套件类不能包含其他测试方法。
  7. @Suite.SuiteClasses注解:更改测试运行器为Suite.class,并把多个测试类以数据的方式传到Suite.SuiteClasses中去。
  8. @Ignore:所修饰的测试方法会被测试运行器忽略。
  9. 测试失败说明:Failure:一般是由于测试结果和预期结果不一致引发的,表示测试的这个点发现了问题error:是由代码异常引起的,它可以产生于测试代码本身的错误,也可以是被测试代码中隐藏的bug

追求代码质量是一个优秀程序员对自我的要求。我们写一段代码、一个方法和一个类,不仅仅说完成了编码,保证代码能正常得跑起来就行了,而且也必须使得代码是优雅和干净的。一般来说正常的情况大家都能考虑到,比较关键和重要的是,我们在写代码时除了能够执行正常业务逻辑以外,还要能考虑和覆盖到各种不同的异常情况。我想在编码时候,考虑正常和异常情况的时间分配比例应该是30%:70%。

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

@Servicepublic class UnitTestServiceImpl implements UnitTestService{ /** * 为了测试,这里直接返回传入的值 */ @Override public String process(String msg) { // TODO Auto-generated method stub return msg; }}

测试类:UnitTestServiceTest.java

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

图片 4示例

/** * 编写接口测试类 * @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; //断言 是否和预期一致 Assert.assertEquals(msg, result); }}
  1. 运行右击,选择 run As --> Junit Test 或者需要debug时,选择Debug As --> Junit Test,运行即可。

图片 5测试结果

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

org.databene

三、断言

官方解释是:编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。

Junit测试框架中Assert类就是实现断言的工具,主要作用如下:单元测试用于判断某个特定条件下某个方法的行为;执行单元测试为了证明某段代码的执行结果和期望的一致。

1、assertEquals(expected, actual):查看两个对象是否相等。类似于字符串比较使用的equals()方法;assertNotEquals(first, second):查看两个对象是否不相等。参数expected为用户期望某一时刻对象的值,actual为某一时刻对象实际的值。如果这两值相等的话(通过对象的equals方法比较),说明代码运行是正确的。2、assertNull:查看对象是否为空。assertNotNull:查看对象是否不为空。3、assertSame(expected, actual):查看两个对象的引用是否相等,类似于使用“==”比较两个对象;assertNotSame(unexpected, actual):查看两个对象的引用是否不相等,类似于使用“!=”比较两个对象。4、assertTrue(String message, boolean condition) 要求condition == true,查看运行的结果是否为true;assertFalse(String message, boolean condition) 要求condition == false,查看运行的结果是否为false。以判断某个条件是真还是假,如果和预期的值相同则测试成功,否则测试失败。

从主观上来说,代码质量一般是跟程序员的专业技能熟练度,比如编程语言(C /Java/go等)、技术框架(Spring/Dubbo/Spring Cloud/Spring Boot等)、设计模式(工厂/抽象/代理模式等),成正比的。但是,对于极为优秀的程序员来说即使能够尽可能地确保自己的千行代码没有缺陷,却不一定能够保证几万行都没有任何缺陷。所以,我们需要借鉴其他的方法来提高自己的代码质量,尽可能少地让潜在的问题暴露在生产环境上。

RESTful API 单元测试

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

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

/** * 编写mock测试服务 * @author oKong * */@RestControllerpublic class DemoController { @GetMapping 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.build(); } @Test public void testDemo() throws Exception { String msg = "this is a mock test"; MvcResult result = this.mockMvc.perform(get.param("msg", msg)).andDo.andExpect.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: 59999 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)

图片 6测试结果

由于配置了print()这个ResultHandler,所以控制台会打印相关参数信息。建议设置此属性,这样就算测试有问题,也能看下具体的参数信息。其他相关mock的用法,此处就不举例了,大家可自行搜索下,比较本章节只是简单示例下用法~

图片 7请求参数信息

  1. 鉴于每次编写控制层测试类时,都需要创建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.build(); }}

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

contiperf

增加功能测试用例和接口单元测试都是能够提高代码质量的方式,各有优劣。本文从编程者的角度出发,更加注重的是代码覆盖测试,毕竟只有写代码的人才能更容易地把控代码中的业务逻辑,能够更好的编写单元测试用例以覆盖正常和异常的业务场景。

Junit常用注解说明

  • @Test 加在待测试的方法前面
  • @Before 带上@Test的方法执行前会执行该方法
  • @After 带上@Test的方法执行完毕后会执行该方法
  • 都应该要求编写单元测试,并发执行30个线程。@BeforeClass 加上这个注解,则该方法会第一个执行,且方法要加上关键词static,是一个static方法
  • @AfterClass 加上这个注解,则该方法最后一个执行,同样,方法要加上关键词static,是一个static方法

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

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

2.1.0

在做单元接口测试时,代码覆盖率常常是被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况。通常来说,我们会关注方法覆盖、语句覆盖、条件覆盖和分支覆盖这几种度量方式。

性能测试示例

0.加入pom依赖包。

 <dependency> <groupId>org.databene</groupId> <artifactId>contiperf</artifactId> <version>2.3.4</version> <scope>test</scope> </dependency>

1.改写UnitTestServiceTest测试类,进入ContiPerfRule题外话:@RuleJunit提供的一个扩展接口注解,其接口类为: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; //断言 是否和预期一致 Assert.assertEquals(msg, result); }}
  1. 控制台会有性能报告,同时访问:target/contiperf-report/index.html,会有图表提示。

控制台输出:

cn.lqdev.learning.springboot.chapter13.service.UnitTestServiceTest.testsamples: 100max: 403average: 41.5median: 15

测试报告:

图片 8测试报告

test

本文第一节主要都是讲了理论,相对比较枯燥。下面这一节将从实践的角度,来一步一步向大家展示如何在Spring Boot工程中对业务代码写单元测试用例。

注解参数说明

@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

图片 9公众号

个人博客:

完整示例:chapter-13

  1. 单元测试类里面使用 @Rule 注解激活 ContiPerf

Spring Boot 1.4.1.RELEASE、JDK1.8

@Rule

在Spring Boot工程中引入单元测试比较简单,只需要简单地在pom文件中引入依赖如下:

public ContiPerfRule i = new ContiPerfRule();

图片 10

  1. 在具体测试方法上使用 @PerfTest 指定调用次数/线程数,使用 @Required 指定每次执行的最长时间/平均时间/总时间等

在工程中引入spring-boot-starter-test后,就会有如下几个库:

@Test

JUnit:Java语言的单元测试框架;

@PerfTest(invocations = 30000, threads = 20)

SpringTest & Spring Boot Test:为Spring Boot程序提供集成测试的工具;

@Required(max = 1200, average = 250, totalTime = 60000)

AssertJ: 一种断言库;

public void test1() throws Exception {

Hamcrest:也是一种断言库,不过更新频度较慢;

snowflakeSequence.nextId();

Mockito :Java程序Mock测试的框架;

}

JsonPath :Xpath在Json中的应用库;

注:

JSONassert:Json的断言库;

1)PerfTest参数

spring-boot-starter-test的pom依赖图如下:

@PerfTest(invocations = 300):执行300次,和线程数量无关,默认值为1,表示执行1次;

图片 11

@PerfTest(threads=30):并发执行30个线程,默认值为1个线程;

对于Spring Boot工程中的Service/Dao层的类来说,创建其单元测试方法比较简单,直接手动创建即可。

@PerfTest(duration = 20000):重复地执行测试至少执行20s。

@RunWith(SpringJUnit4ClassRunner.class)

2)Required参数

@SpringBootTest(classes = OpHuaweiAgentApplication.class)

@Required(throughput = 20):要求每秒至少执行20个测试;

@Slf4j

@Required(average = 50):要求平均执行时间不超过50ms;

public class VmServiceTest {

@Required(median = 45):要求所有执行的50%不超过45ms;

private ObjectMapper mapper = new ObjectMapper();

@Required(max = 2000):要求没有测试超过2s;

private static final String JSON_CREATE_VM_REQUEST = "/vm/createVmReq.json";

@Required(totalTime = 5000):要求总的执行时间不超过5s;

@Autowired

@Required(percentile90 = 3000):要求90%的测试不超过3s;

private VmService vmService;

@Required(percentile95 = 5000):要求95%的测试不超过5s;

@Before

@Required(percentile99 = 10000):要求99%的测试不超过10s;

public void setUp() throws Exception {

@Required(percentiles = "66:200,96:500"):要求66%的测试不超过200ms,96%的测试不超过500ms。

//测试用例数据准备

3)@PerfTest @Required 可以加在类上面,表示所有方法的默认配置

//code here

4. 在测完之后,在本地的target下生成contiperf-report目录,里面有个index.html文件,即为测试结果。

}

@Test

public void create() throws Exception {

String createBody = getResource(JSON_CREATE_VM_REQUEST);

JSONObject body = JSONObject.parseObject(createBody);

HwTokenWrapper token = getDomainToken();

String projectId = token.getToken().getProject;

CreateJobRespDto respDto = vmService.create(token.getId(), projectId, body);

assertTrue(null != respDto.getJobId;

log.debug("create vm job创建成功,jobId:{}", respDto);

}

@After

public void cleanUp() throws Exception(){

//测试数据清理

//code here

}

如上面的对Service层的单元测试用例代码可见,在带有@Before注解的方法setUp中完成对测试用例的数据准备,可以提前在测试环境数据库中插入测试用例所需依赖的测试局数据。在@Test注解的方法—create是单元测试真正执行的方法,示例中使用提前组织好的创建主机规格的Json数据作为参数调用被测试的Service层的VmService方法,执行创建主机的验证。同时使用断言机制,来判断返回结果是否跟预期的一致。其中,准备好的Json数据放在SpringBoot工程的src/test/resources下面。最后在,@After注解的方法cleanUp下执行提前插入数据的回滚和清理。

对Service/Dao层的类进行接口单元测试还是比较简便的。然而,一般的SpringBoot工程都需要对外部提供Api接口,因此有必要对Controller层进行单元测试以保证控制器执行的业务逻辑正确,这时候就得用到MockMvc了。使用MockMvc可以使得开发或者测试不必再借助postman这种Http调试工具进行手动测试,既提高测试的效率,也能够反复跑单元测试用例来进行回归验证。

Spring Test框架中的MockMvc实现了对Http请求的模拟,能够直接通过网络的形式,转换到Controller层的Api调用,这样在提高测试效率的同时可以不依赖外部环境。

@RunWith(SpringJUnit4ClassRunner.class)

@SpringBootTest(classes = OpHuaweiAgentApplication.class)

@WebAppConfiguration

@Slf4j

public class OrderManageControllerTest extends AbstractTest {

//此处API URL为示例代码

public static final String GENERATE_ORDERID_API_URL = "/rest/xxxxxxxxx";

//此处为鉴权的Json测试数据

private static final String JSON_AUTH_TOKEN_REQ = "/api/order/authTokenReq.json";

@Autowired

private WebApplicationContext context;

private MockMvc mvc;

@Before

public void setUp() {

//测试用例数据准备

//code here

this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();

}

@Test

public void generateOrderIdTest() throws Exception {

JSONObject jsonObject = JSONObject.parseObject(getResource(JSON_AUTH_TOKEN_REQ));

MockHttpServletRequestBuilder builder = post(GENERATE_ORDERID_API_URL). header("X-Auth-Token", jsonObject.getString;

MvcResult result = mvc.perform.andReturn();

assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus;

}

@After

public void cleanUp() throws Exception(){

//测试数据清理

//code here

}

从上面对Controller层Api接口的单元测试示例代码可见,在带有@Before注解的setUp方法中,通过MockMvcBuilders工具类使用注入的WebApplicationContext上下文对象创建MockMvc对象。这里,MockMvc对象提供一组工具函数用来执行assert判断,都是针对web请求的判断。这组工具的使用方式是函数的链式调用,允许程序员将多个测试用例链接在一起,并进行多个判断。在带有@Test注解的generateOrderIdTest测试方法中,先加载提前准备好的鉴权请求JsonObject对象,然后MockMvc对象执行相应的post请求,其中参数为带有Header头的MockHttpServletRequestBuilder对象。最后,通过assertEquals断言机制来确认接口返回是否为Http响应的正确编码。如同之前的一样,@After注解的方法cleanUp下执行提前插入数据的回滚和清理。

通过上面的内容,可以在Spring Boot工程中完成对Controller/Service/Dao层的添加单元测试用例,但仅限于此只能通过单元测试用例的结果来判断用例正确与否,而无法来判断测试的其他度量指标,比如本文前面提到的方法覆盖、语句覆盖、条件覆盖和分支覆盖等。因此,这节通过引入第三方组件—Cobertura来完成这一目标。

Cobertura 是一种开源的代码覆盖率检测工具,它通过检测基本的代码,并观察在测试包运行时执行了哪些代码和没有执行哪些代码,并最终以html或者xml的格式来呈现最终测试的度量指标结果(比如分支覆盖率和代码行覆盖率)。

在Spring Boot工程的pom文件中添加Cobertuar插件的配置如下:

图片 12图片 13

在Spring Boot工程目录下执行以下maven命令—“mvn cobertura:cobertura”,执行完后会在target目录里找到site目录,用浏览器打开里面的index.html,这就是测试用例执行完后cobertura-maven-plugin得出的覆盖率报告。如图下所示:

图片 14图片 15

仅在本地对Spring Boot工程执行Cobertura的maven命令,并不能很好的实现自动持续集成的目标。这一节主要将介绍如何在Jenkins工具中一步步集成Cobertura插件并完成Spring Boot工程的代码覆盖率测试报告输出。

1、首先需要在Jenkins工具上完成Cobertura插件的安装。

图片 16

2、配置jenkins工具,修改maven的执行命令,这里主要是添加cobertura执行命令clean cobertura:cobertura package。

图片 17

3、在Add post build action这个配置项中选择如下Publish Cobertura Coverage Report:

图片 18

4、这一步中需要选择一个配置项,该配置项目是最终cobertura生成xml/html report的路径,在示例中的路径为**/target/site/cobertura/coverage.xml。

图片 19

5、最后,重新build该项目,即可在项目中看到本工程代码覆盖率的测试用例报告了:

图片 20图片 21

本文从代码质量与单元测试用例方面切入,先介绍了如何在Spring Boot工程中完成各层(Controller Api/Service/Dao层)的接口单元白盒测试,随后介绍了如何在Spring Boot工程中集成Cobertura插件,并利用Jenkins工具进行自动化持续集成以产生代码覆盖率的测试报告。限于笔者的才疏学浅,对本文内容可能还有理解不到位的地方,如有阐述不合理之处还望留言一起探讨。

个人专属的公众号,欢迎关注一起交流和学习二维码如下:

图片 22匠心独运的博客

编辑:编程 本文来源:都应该要求编写单元测试,并发执行30个线程

关键词: 澳门新濠3559