基于 Junit 的接口自动化测试框架实现!

基于 Junit 的接口自动化测试框架实现

分层的自动化测试

5~10 年前,我们接触的自动化测试更关注的是 UI 层的自动化测试,Mercury 的 WinRunner/QTP 是那个时代商业性自动化测试产品的典型代表,在那个时代大家单纯想的都是能用一个自动化操作的工具替代人力的点击,商业化或是私有化框架大行其道。

而分层的自动化测试倡导产品的不同阶段(层次)都需要自动化测试。在《Google 软件测试之道》中,在 Google 70% 的投入为单元测试(小型测试),20% 为接口 / 集成测试(中型测试),10% 为 UI 层的自动化测试(大型测试),也就是大家熟悉的金字塔模型,越往上自动化实现难度越大,投入产生的收益也越低(需要强调的是,UI 层的自动化测试作为最接近用户操作的测试,仍然有其存在的意义和场景)。

接口测试的意义

接口测试是验证两个或多个模块应用之间的交互(通常是采用接口的方式),测试的重点是要检查数据的交换,传递和控制管理过程,还包括处理的次数。

接口测试的核心战略在于:以保证系统的正确和稳定为核心,以持续集成为手段,提高测试效率,提升用户体验,降低产品研发成本。

接口测试要为代码的编写保驾护航,增强开发人员和测试人员的自信,让隐含的 BUG 提前暴露出来,要让开发人员在第一时间修复 BUG,要让业务测试人员在测试的时候更加顺手,最大限度得减少底层 BUG 的出现数量,要让产品研发的流程更加敏捷,要缩短产品的研发周期,最后在产品上线以后,要让用户用得更加顺畅,要让用户感觉产品服务零缺陷。

不同于单元测试,接口测试本质上还是一种黑盒的测试,所以非常适合专职测试工程师去参与和覆盖。

接口测试框架选型

  1. 目前接口测试框架的选型,最常见的方法是采用 JMeter、soapUI、postman、robotframework 等 UI 化的接口测试框架来做。

好处是业务测试人员可以不用或很少写测试代码,入门门槛低,前几年有很多公司都曾经开发过类似的测试框架,有前端有后端,专职的测试开发人员维护,业务测试人员只需要知道怎么操作而不需要参与具体 coding。
这种方法看起来非常高大上,但实际的问题是执行过程中主要的工作变成了测试框架的维护,非常依赖专职测试开发人员的设计和开发能力,每增加一种新的接口协议(比如 Dubbo、Hessian 或者内部自定义的协议)就需要在框架上增加支持;更致命的是一旦核心测试开发人员出现流动,就很容易造成整个接口测试体系的崩塌;另外对业务测试人员的技能成长也并不公平,个人已面试过太多只会使用某大公司 XXX 测试框架却完全不了解具体实现方式的工程师。

《Google 软件测试之道》中早已有过预言,保密和私有化的基础测试设施并不能获得想象中的好处,这种方式意味着昂贵和迟缓,即使在公司内部的不同项目之间也很难做到复用。未来的测试基础设施必然是建立在共享代码和开源框架的基础上,测试开发人员需要更多的利用开源项目并为之贡献。最近重读了一次这本几乎改变软件测试行业的书籍,发现里面的预言都是如此准确,当然也可以认为国内整个行业都正参照 Google 的方式在进行演变。

  1. 使用 Junit、Testng 等 Java 接口框架,直接编写测试代码去测试,同时对一些重复性的工作抽象建立基础库或方法。

有点类似于单元测试,这种方法扩展性好实现灵活,作为程序员可以用代码实现灵活的场景组织和功能,只要稍微二次开发一下,但需要测试工程师有一定的编码基础。 

这种方式在前几年实施的难度还是比较大,因为在市场上要找到懂 Java 代码的测试工程师都寥寥无几,但在对测试工程师开发能力要求越来越多的今天实施难度已没有想象中困难,Java/Python 等语言的编码能力也已成为我们团队招聘时的基本要求。

另外提下,这里使用 Java 而不用其他语言的原因,主要是团队的技术储备 Java 是强项 , 拥有丰富的开源测试库,而且一般互联网公司的产品基本都是采用 Java 框架进行开发,和开发团队技术栈保持一致非常有必要性。

GTest 接口测试框架

有很多公司做了各种不同的接口框架,都是基于自己公司的业务基础设计开发。我们基于自己的业务特点也封装了自己的接口测试框架 gtest-framework,在开发人员的单元测试中也正逐渐使用。

gtest-framework 要做的事情:

  1. 前置数据准备和自动清理。
  2. 常见接口协议的实现和封装。
  3. 依赖注入配置方式的支持。
  4. 如文件、图片、xml、字符等各类通用处理方法的集成。
  5. 断言方式的扩展等。

接口测试关键实践

1. 数据准备

接口测试的数据准备,一般是指数据库的数据准备,有时候还包括文件和缓存的数据准备。具体实现可以从下面几个方面去考虑

(1)硬编码的方式准备测试数据,在写测试代码的时候,使用到什么数据就插入什么数据。为了避免数据重复,很多人会习惯于使用随机字符或随机数(这种方法可能造成测试用例不稳定,尽量避免)。

(2)可以直接通过调用其他 API 的方式准备测试数据,这种情况在测试最上层服务的时候比较有用,比如测试购买商品,就需要准备要购买的商品数据,购买商品的用户数据,这个时候,可以直接调用生成商品的 API 和生成用户的 API 直接生成测试数据。此方法实现简单,但前提是需要具备相应的 API 并且此 API 功能正确。

(3)使用 Excel 或 XML 准备测试数据,这种准备测试数据的方式,主要针对对象数据的准备,比如可以将一条商品数据对应 Excel 中的一条数据,因为一般开发都会使用 pojo 映射,而在准备测试数据的时候,这些 pojo 对象属性的设置往往是重复和大工作量的,用 Excel 或 XML 方式准备,则可以减少在代码当中重复去准备这些数据。

一般我们使用的是 2/3 两种方式,其中 3 这种方式主要利用 Dbunit、Spring-test、Unitils 等测试框架的特性经二次开发增加自定义注解,很轻松的导入 Excel 或 XML 格式的文件并在测试完成后对数据进行自动回滚。

/** 
* @ClassName: TestJdbcDataSet 
* @Description: 采用自定义 TestDataSet 注解方式准备测试数据,推荐。
* @author Cay.Jiang   
* @date 2017 年 7 月 10 日 上午 9:10:29 
*  
*/
public class TestJdbcDataSet extends BaseCase{Map<String, Object> args = new HashMap<String, Object>();@Test@TestDataSet(locations={"/tmp/domaininfo.xls"},dsNames={"mysqlDataSource"})public void test01_mysql(){args.put("selfdomain", "baidupc2");List<Map<String, Object>> result=JdbcUtil.queryData(mysqlJdbcTemplate, "domaininfo", args);System.out.println(result);assertEquals(" 合作商接入名称 ",result.get(0).get("remark"));}
}

上面代码中的 /tmp/domaininfo.xls 参见:domaininfo.xls ,其中 Excel 格式以 Sheet 名为表名,第一行定义了字段名称,其余行为对应的数据。

多数据集:
@TestDataDataSet(locations={"Data1.xls","Data2.xls"},dsNames={"dsNameA","dsNameB"}),Data1.xls 的数据会插入 dsNameA 所指的数据库中,Data2.xls 的数据会插入 dsNameB 所指的数据库中

2. 断言

常见的断言方式有 JUnit 自带的 Assert 和 Hamcrest。JUnit 自带的断言方法功能十分有限只能满足最基本的需求。Hamcrest 相对来讲功能丰富一些,但是该库已经多年不更新。而且 Hamcrest 和 JUnit 自带的断言方法一样,有个致命的缺点,就是当一个 case 中有多个断言时,如果其中一个断言失败,那么在它之后的断言都不会执行。这里向大家推荐一款新的断言神器 AssertJ。

AseertJ: 号称流式断言。什么是流式,常见的断言器一条断言语句只能对实际值断言一个校验点,而 AseertJ 支持一条断言语句对实际值同时断言多个校验点,这样使得断言的语句更加简洁适合阅读。AseertJ 还支持一次性执行所有断言,然后收集所有失败的断言一起反馈。当然除此之外 AseertJ 还有很多其他特性,可以参考官方文档慢慢挖掘。下面将举例说明一下 AseertJ 的优势:

public class TestCase extends BaseCase{UserProfileBO user = new UserProfileBO();@Beforepublic void init(){user.setAddress(" 杭州 ");user.setMobile("1386800000");user.setUserName(" 测试账号 ");}/** JUnit 内置的断言* * 1、其中一个断言失败后,后面所有断言将不会执行。* 2、支持的断言方法较少* */@Testpublic void testAssertJUnit(){Assert.assertEquals(" 地址 ",user.getAddress()," 宁波 ");Assert.assertEquals(" 手机 ",user.getMobile(),"13868000000");Assert.assertEquals(" 手机 ",user.getUserName()," 测试 ");Assert.assertNotNull(user.getMobile());Assert.assertTrue(user.getMobile().startsWith("138"));Assert.assertTrue(user.getMobile().length() == 11);}/** Hamcres 断言* * 1、其中一个断言失败后,后面所有断言将不会执行。* 2、支持的断言方法丰富,但是已经多年不更新。* */@Test public void testHamcrestMatchers() {  MatcherAssert.assertThat(user.getAddress(), equalTo(" 宁波 "));  MatcherAssert.assertThat(user.getMobile(), equalTo("13868000000"));  MatcherAssert.assertThat(user.getUserName(), equalTo(" 测试 "));  MatcherAssert.assertThat(user.getMobile(), allOf(is(nullValue()),startsWith("136")));  }/** AssertJ 断言* * 1、支持所有断言执行后,失败断言统一反馈。* 2、支持的断言方法丰富。* 3、支持流式断言,方便阅读。* */@Testpublic void testAssertJ(){// 断言集合,执行所有断言后,失败断言统一反馈。SoftAssertions.assertSoftly(softly -> {softly.assertThat(user.getAddress().equals(" 宁波 "));softly.assertThat(user.getMobile().equals("13868000000"));softly.assertThat(user.getUserName().equals(" 测试 "));});// 流式断言Assertions.assertThat(user.getMobile()).isNotNull().startsWith("136").hasSize(11);}
}

3.jenkins 集成接口测试

(1)设置测试代码的仓库地址及身份信息

(2)设置 maven 运行参数

希望执行部分接口用例,可以通过-Dtest=XXX(测试类名) 的方式执行指定的 case,多个类名用逗号 “,” 隔开

(3)设置 Job 执行机制,下图表示每天定时执行

4.pipeline 参数注入

前面写 pipeline 的时候提过,我们的接口测试代码是需要支持外部参数注入的,比如测试的服务地址,不同的分支代码可能部署在不同测试服务器上,需要在 pipeline 中通过参数化的方式驱动对不同的服务器进行接口测试。

这里我们可以使用 maven 的-D(Properties 属性)来实现,举例如下:

(1)dubbo 使用 properties 配置文件,但具体参数使用 ${key}占位符方式打包替换

(2)maven 的 pom 文件中指定对应配置文件中的参数值 (此处指定的参数值会被通过 maven -D 传递过来的参数值覆盖)

此处注意:还需要启动 resources 的 filter 过滤器

(3) 使用 maven 命令行设置属性值

并对该值进行参数化支持 pipeline 传参

5.Pipeline 代码实现

      stage(' 接口自动化测试 ') {steps{echo "starting interfaceTest......"script {// 为确保 jetty 启动完成,加了一个判断,确保 jetty 服务器启动可以访问后再执行接口层测试。timeout(5) {waitUntil {try {// 确保 jetty 服务的端口启动成功sh "nc -z ${serverIP} ${jettyPort}"//sh "wget -q http://${serverIP}:${jettyPort} -O /dev/null"return true} catch (exception) {return false}}}// 将参数 IP 和 Port 传入到接口测试的 job,需要确保接口测试的 job 参数可注入build job: ITEST_JOBNAME, parameters: [string(name: "dubbourl", value: "${serverIP}:${params.dubboPort}")]}}}

测试代码规范 (仅供参考)

  1. 测试项目命名规范

接口测试:
一般需要独立测试项目,测试项目的命名规则为:“test-“+ 被测试的项目名,如 test-kano。
单元测试:
不需要重建独立测试项目,和开发代码放在同一项目即可。

  1. 测试目录定义规范

测试代码统一放在测试项目的 “src/test/java” 下。
测试配置文件统一放置在 “src/test/resources” 下。

  1. 包名定义规范

与被测试项目中的包名一致。

  1. 测试类命名规范

测试类的命名规则是:以 Test 开头,以它要测试的对象的名称结尾,例如
Test+ 被测试的业务、Test+ 被测试的接口、Test+ 被测试的类
另外一种方式是:以 Test 结尾,以它要测试的对象的名称开头,例如
被测试的业务 +Test、被测试的接口 +Test、被测试的类 +Test
视个人习惯而定,为了 case 定位方便,目前测试团队一般用第一种。

  1. 测试用例命名规范

测试用例的命名规则是:test+ 用例操作 _ 条件状态,统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
单词的约定与测试类命名同

  1. 接口测试代码常见约束

(1)数据清理和构造

  • @BeforeClass @Before 中做数据准备等相关操作:加载测试类以前需要加载所有测试用例共同的场景数据,同时在运行单个测试用例的时候加载特别的测试数据。
  • @AfterClass @After 中做测试数据清理等相关操作:在执行完相关测试以后清理用例现场。

(2)断言

  • 不要做无谓的断言
    在测试模式下,有时会情不自禁的滥用断言。这种做法会导致维护更困难,需要极力避免。仅对测试方法名指示的特性进行明确测试,因为对于一般性代码而言,保证测试代码尽可能少是一个重要目标。
  • 使用显式断言方式
    应该总是优先使用 assertEquals(a, b) 而不是 assertTrue(a == b), 因为前者会给出更有意义的测试失败信息。在事先不确定输入值的情况下,这条规则尤为重要。
  • 断言的参数顺序要合适

(3)测试用例保持独立

  • 确保测试代码独立于项目代码之外。
  • 为了保证测试稳定可靠且便于维护,测试用例之间决不能有相互依赖,也不能依赖执行的先后次序。

(4)测试代码要考虑错误处理

  • 如果前面的代码执行失败,后续语句会导致代码崩溃,剩下的测试都无法执行。任何时候都要为测试失败做好准备,避免单个失败的测试项中断整个测试套件的执行。
  • 不要写自己的 catch 代码块,即只有 test 失败的情况,不应该存在 catch 情况。

结语

接口测试是一个非常庞杂的体系,很难用一篇文章阐述清楚,其他实践如接口测试覆盖率统计、标准协议页面化测试平台(提供给没有编码能力的产品或测试人员使用)、maven 项目骨架建立标准化测试工程、dubbo/hessian/restful/webservice 等接口协议具体实现等等限于篇幅也没有做具体阐述。期待大家一起探讨。

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/152915.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【教3妹学编辑-mysql】详解数据库三大范式

什么是范式 简单地理解就是&#xff1a;数据库设计时遵循的规范 三大范式 数据库三大范式包含&#xff1a;1、第一范式(1NF)&#xff1b;2、第二范式(2NF)&#xff1b;3、第三范式(3NF)。其中&#xff0c;第一范式(1NF)的要求是属性不可分割&#xff0c;第二范式(2NF)的要求是…

Java基础-----正则表达式

文章目录 1.简介2.目的3.学习网站4.常用匹配字符5.String类中用到正则表达式的方法 1.简介 又叫做规则表达式。是一种文本模式&#xff0c;包括普通字符和特殊字符&#xff08;元字符&#xff09;。正则使用单个字符来描述、匹配一系列某个句法规则的字符串&#xff0c;通常用…

[架构之路-247]:目标系统 - 设计方法 - 软件工程 - 结构化方法的基本思想、本质、特点以及在软件开发、在生活中的应用

目录 前言&#xff1a; 一、什么是非结构化方法 1.1 什么是非结构化方法 1.2 非结构化方法的适用场合 二、什么是结构化方法 1.1 结构化方法诞生的背景&#xff1a;软件规模发展&#xff1a;大规模、复杂系统的需要 1.2 概述 1.3 主要特点与核心思想 三、结构化方法在…

【Web】Flask|Jinja2 SSTI

目录 ①[NISACTF 2022]is secret ②[HNCTF 2022 WEEK2]ez_SSTI ③[GDOUCTF 2023] ④[NCTF 2018]flask真香 ⑤[安洵杯 2020]Normal SSTI ⑥[HNCTF 2022 WEEK3]ssssti ⑦[MoeCTF 2021]地狱通讯 ①[NISACTF 2022]is secret dirsearch扫出/secret 明示get传一个secret ?…

Wireshark抓包:理解TCP三次握手和四次挥手过程

TCP是一种面向连接、端到端可靠的协议&#xff0c;它被设计用于在互联网上传输数据和确保成功传递数据和消息。本节来介绍一下TCP中的三次握手和四次挥手。 文章目录 1 TCP头部格式2 wireshark抓包分析2.1 SEQ和ACK2.2 三次握手2.3 四次挥手 3 程序 1 TCP头部格式 TCP头部占据…

【FPGA】Verilog:实现 RS 触发器 | Flip-Flop | 使用 NOR 的 RS 触发器 | 使用 NAND 的 RS 触发器

目录 0x00 RS 触发器&#xff08;RS Flip-Flop&#xff09; 0x01 实现 RS 触发器 0x02 使用 NOR 的 RS 触发器 0x03 使用 NAND 的 RS 触发器 0x00 RS 触发器&#xff08;RS Flip-Flop&#xff09; 触发器&#xff08;Flip-Flop&#xff09;是一种带有时钟的二进制存储设备…

C/C++多级指针与多维数组

使用指针访问数组 指针类型的加减运算可以使指针内保存的首地址移动。 指针类型加n后。首地址向后移动 n * 步长 字节。 指针类型减n后。首地址向前移动 n * 步长 字节。 步长为指针所指向的类型所占空间大小。 例如&#xff1a; int *p (int *)100;p 1&#xff0c;结果为首…

Autox.js和Auto.js4.1.1手机编辑器不好用我自己写了一个编辑器

功能有 撤销 重做 格式化 跳转关键词 下面展示一些 内联代码片。 "ui"; ui.layout( <drawer id"drawer"><vertical><appbar><toolbar id"toolbar"title""h"20"/></appbar><horizontal b…

Linux环境搭建(tomcat,jdk,mysql下载)

是否具备环境&#xff08;前端node&#xff0c;后端环境jdk&#xff09;安装jdk,配置环境变量 JDK下载 - 编程宝库 (codebaoku.com) 进入opt目录 把下好的安装包拖到我们的工具中 把解压包解压 解压完成&#xff0c;可以删除解压包 复制解压文件的目录&#xff0c;配置环境变量…

【opencv】debug报错HEAP CORRUPTION DETECTED

运行至第一句涉及矩阵运算的代码&#xff08;如cv::multiply&#xff09;时报错 HEAP CORRUPTION DETECTED: after Normal block (#45034) at 0x000001BDC586F0E0. CRT detected that the application wrote to memory after end of heap buffer.release下不会报错&#xff0…

PDF控件Spire.PDF for .NET【转换】演示:自定义宽度、高度将 PDF 转 SVG

我们在上一篇文章中演示了如何将 PDF 页面转换为 SVG 文件格式。本指南向您展示如何使用最新版本的 Spire.PDF 以及 C# 和 VB.NET 指定输出文件的宽度和高度。 Spire.Doc 是一款专门对 Word 文档进行操作的 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻…

MyBatis 快速入门

MyBatis 快速入门 前言什么是 MyBatis简介核心特性使用示例配置文件Mapper 接口SQL 映射文件使用 MyBatis 如果大家对以上的导读很懵怎么办&#xff01;没关系 往下阅读&#xff01; 1. MyBatis 介绍1.1. 什么是MyBatis1.2. 持久层1.3. 框架1.4. JDBC 弊端1.5.…

如何解决网站被攻击的问题:企业网络攻防的关键路径

在当今数字化时代&#xff0c;企业面临着不断升级的网络威胁&#xff0c;网站遭受攻击的风险也与日俱增。解决网站被攻击的问题对企业发展至关重要&#xff0c;不仅关系到企业的信息安全&#xff0c;也直接影响到企业的声誉和利益。从企业发展的角度出发&#xff0c;我们将探讨…

科技云报道:全球勒索攻击创历史新高,如何建立网络安全的防线?

科技云报道原创。 最简单的方式&#xff0c;往往是最有效的&#xff0c;勒索软件攻击就属于这类。 近两年&#xff0c;随着人类社会加速向数字世界进化&#xff0c;勒索软件攻击成为网络安全最为严重的威胁之一。今年以来&#xff0c;勒索软件攻击在全球范围内呈现快速上升态…

HDCTF2023 - Reverse方向全WP

文章目录 [HDCTF 2023]easy_re[HDCTF 2023]easy_asm[HDCTF 2023]fake_game[HDCTF 2023]enc[HDCTF 2023]double_code[HDCTF 2023]买了些什么呢[HDCTF2023]basketball [HDCTF 2023]easy_re UPX壳&#xff0c;脱壳 一个base64编码。 [HDCTF 2023]easy_asm ida打开后可以看到xor 10…

数据库管理工具,你可以用Navicat,但我选DBeaver!

大家好&#xff0c;我是豆小匠。数据库GUI工具哪家强&#xff0c;众人遥指Navicat。 可是Navicat老贵了。 如果公司有正版授权的还好&#xff0c;如果没有正版授权&#xff0c;还不给你用盗版&#xff0c;那才叫绝绝子。 好了&#xff0c;主角登场&#xff0c;DBeaver&#x…

基于安卓android微信小程序美容理发店预约系统app

项目介绍 为美容院设计一个系统以减少员工的工作量就成为了想法的初始状态。紧接着对美容院进行进一步的调查发现我的想法已然落后。基本上每个美容院都以有了自己的信息系统&#xff0c;并且做的已经较完善了。 在这时我突然想到&#xff0c;现在关注美容养生的人越来越多&am…

智能井盖传感器建设信息化时代智慧城市

近年来随着信息技术的快速发展和城市化进程的加速推进&#xff0c;智慧城市的概念逐渐成为现实。作为智慧城市生命线建设中的重要组成部分&#xff0c;智能井盖传感器的应用正在为城市的可持续发展和居民的生活质量提供新的解决方案。 智能井盖传感器能够实时监测井盖状态&…

vue3父组件提交校验多个子组件

实现功能&#xff1a;在父组件提交事件中校验多个子组件中的form 父组件&#xff1a; <script setup lang"ts">import {ref, reactive} from vueimport childForm from ./childForm.vueimport childForm2 from ./childForm2.vuelet approvalRef ref()let ap…

图像分割方法

常见的图像分割方法有以下几种&#xff1a; 1.基于阈值的分割方法 灰度阈值分割法是一种最常用的并行区域技术&#xff0c;它是图像分割中应用数量最多的一类。阈值分割方法实际上是输入图像f到输出图像g的如下变换&#xff1a; 其中&#xff0c;T为阈值&#xff1b;对于物体的…