java接口自动化测试框架-java自动生成代码框架
前言
这半个月基本都在出差和各种公司业务上度过,难得有时间整理一些测试技术的事情。 周末有一些免费代码可以填坑。 持续交付/持续集成系列文章,不仅是为了分享给坛子里的同行,更是对个人的一种自我思考和鞭策。 总的来说,我觉得这次论坛的气氛还是比较清爽的。 希望在人气飞速提升的同时,能保持初心,坚持做一个纯技术分享交流的平台。
分层自动化测试
5~10年前,我们接触到的自动化测试更多的集中在UI层的自动化测试。 Mercury的WinRunner/QTP是那个时代商用自动化测试产品的典型代表。 自动化工具取代人工点击,商业化或私有化框架大行其道。
而分层自动化测试提倡在产品的不同阶段(层次)都需要进行自动化测试。 在《谷歌软件测试之道》中,谷歌70%的投入是单元测试(小测试),20%是接口/集成测试(中测试),10%是UI层的自动化测试(大测试)。 就是大家熟悉的金字塔模型。 层级越高,实现自动化的难度越大,投资收益越低(需要强调的是,作为最接近用户操作的测试,UI层的自动化测试还是有它的意义和场景的。 ).
接口测试的意义
接口测试是验证两个或多个模块应用程序之间的交互(通常以接口的形式)。 测试的重点是检查数据交换、传输和控制管理过程,包括处理的次数。
接口测试的核心策略是:以保证系统的正确性和稳定性为核心,以持续集成为手段,提高测试效率,改善用户体验,降低产品开发成本。
接口测试应该为代码的编写保驾护航,增强开发和测试人员的信心,提前暴露隐藏的bug,让开发人员在第一时间修复bug,让业务测试人员在测试的时候更舒服,尽量减少底层bug的数量,以让产品开发过程更加敏捷,缩短产品开发周期,最终在产品上线后,让用户更顺畅地使用,让用户感受到产品服务零缺陷。
与单元测试不同,接口测试本质上是一种黑盒测试,因此非常适合专职测试工程师参与和覆盖。
接口测试框架选择
1、目前界面测试框架的选择最常用的方法是使用jmeter、soapUI、postman、robotframework等基于UI的界面测试框架。
优点是业务测试人员不需要或很少编写测试代码,进入门槛低。 过去几年,很多公司都开发了类似的测试框架,有前端和后端,专职测试开发人员维护,业务测试人员只需要知道如何操作,不需要参与具体的编码。
这种方式看似很高大上,但实际问题在于,实现过程中的主要工作变成了测试框架的维护,严重依赖于专职测试开发人员的设计和开发能力。 每增加一个新的接口协议(比如dubbo、hessian或者内部定制的协议)都需要给框架增加支持; 更致命的是,核心测试开发人员一旦流动,很容易造成整个接口测试系统的崩溃; 这不公平。 我个人面试过太多只用大公司的XXX测试框架却完全不了解具体实现方法的工程师。
《谷歌软件测试之道》中早就预言,基础测试设施的保密和私有化无法获得想象中的收益。 这种方法成本高、速度慢,甚至在公司内部的不同项目之间也很难做到。 重复使用。 未来的测试基础设施必须基于共享代码和开源框架,测试开发人员需要更多地利用和贡献开源项目。
最近重读这本在四五年前几乎改变了软件测试行业的书,发现里面的预言竟然如此准确。 当然,也可以认为整个国内行业都在像谷歌一样在进化。
2、使用junit、testng等java接口框架直接编写测试代码进行测试,同时对一些重复性的工作进行抽象和建立基础库或方法。
它有点类似于单元测试。 这种方法是可扩展和灵活的。 作为程序员,可以用代码实现灵活的场景组织和功能。 你只需要做一点二次开发,但测试工程师需要有一定的编码基础。
这种方法在前几年实现起来还是比较困难的,因为市场上懂java代码的测试工程师很少,但是在今天实现起来并不难,因为对开发能力的要求越来越高测试工程师。 难点,java/python等语言的编码能力也成为了我们团队招聘的基本要求。
另外这里之所以用java而不是其他语言主要是因为团队的技术储备java是它的强项,有丰富的开源测试库,一般互联网公司的产品基本都是用java开发的框架,和开发团队的技术栈保持一致是非常有必要的。
我们封装的接口测试框架
很多公司做了各种接口框架,都是根据自己公司的业务基础设计开发的。 根据自身的业务特点(为避广告嫌疑,具体公司信息我尽量不提),我们也封装了自己的接口测试框架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也支持一次性执行所有断言java接口自动化测试框架,然后收集所有失败的断言进行反馈。 当然除此之外,AseertJ还有很多其他的特性,大家可以参考官方文档慢慢挖掘。 下面将说明AseertJ的优势:
public class TestCase extends BaseCase{
UserProfileBO user = new UserProfileBO();
@Before
public void init(){
user.setAddress("杭州");
user.setMobile("1386800000");
user.setUserName("测试账号");
}
/*
* JUnit 内置的断言
*
* 1、其中一个断言失败后,后面所有断言将不会执行。
* 2、支持的断言方法较少
*
*/
@Test
public 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、支持流式断言,方便阅读。
*
*/
@Test
public 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.管道参数注入
前面在写流水线的时候提到,我们的接口测试代码需要支持外参注入,比如测试的服务地址。 不同的分支代码可能部署在不同的测试服务器上,需要在流水线中进行参数化。 用于接口测试的不同服务器。
这里我们可以使用maven的-D(Properties属性)来实现,例如:
(1)dubbo使用了properties配置文件,只是具体的参数进行了封装,换成了${key}占位符
(2)在maven的pom文件中指定对应配置文件中的参数值(这里指定的参数值会被maven -D传过来的参数值覆盖)
这里注意:还需要启动资源的filter过滤器
(3)使用maven命令行设置属性值
并将值参数化以支持pipeline参数传递
5.流水线代码实现
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
单元测试:
不需要重新构建一个独立的测试项目,只需要和开发代码放在同一个项目中即可。
2.测试目录定义规范
测试代码放在测试项目的“src/test/java”下。
测试配置文件统一放在“src/test/resources”下。
3.包名定义规范
与被测项目中包名一致
4.测试类命名规范
测试类的命名规则是:以Test开头,以被测对象名称结尾,例如
Test+被测业务、Test+被测接口、Test+被测类
另一种方法是:以 Test 结尾,以它正在测试的对象的名称开始,例如
被测业务+测试,被测接口+测试,被测类+测试
根据个人习惯java接口自动化测试框架,为了方便case定位,目前的测试团队一般采用第一种方式。
5. 测试用例命名规范
测试用例的命名规则为:test+case operation_condition state,统一使用lowerCamelCase风格,必须遵循驼峰命名法。
单词约定与测试类名相同
6.接口测试代码常用约束
(1) 数据清洗与结构化
@BeforeClass @Before 做数据准备等相关操作
--加载测试类,用于加载所有测试用例的公共场景数据,运行单个测试用例时加载特殊测试数据
@AfterClass @After 做测试数据清洗等相关操作
--执行相关测试后清理用例站点
(2)断言
--不要做不必要的断言
在测试模式下,有时你会忍不住滥用断言。 这种做法会使维护更加困难,需要尽量避免。 只显式测试测试方法名所指示的特性,因为对于一般代码来说,保证测试代码尽可能少是一个重要的目标
-- 使用显式断言
assertEquals(a, b) 应始终优先于 assertTrue(a == b) 使用,因为前者会给出更有意义的测试失败消息。 当事先不知道输入值时,这条规则尤为重要
--assertion的参数顺序要合适
(3) 测试用例保持独立
--确保测试代码独立于项目代码
--为了保证测试稳定、可靠和易于维护,测试用例之间不能相互依赖,也不能有执行顺序。
(4) 测试代码要考虑错误处理
-- 如果前面的代码执行失败,后面的语句会导致代码崩溃,剩下的测试无法执行。 随时做好测试失败的准备,避免单个失败的测试项打断整个测试套件的执行
--不要写自己的catch代码块,即只有测试失败,应该没有catch的情况
结语
接口测试是一个非常复杂的系统,很难在一篇文章中讲清楚,在持续交付系统中起着非常重要的作用。
其他实践如接口测试覆盖率统计、标准协议页面测试平台(为没有编码能力的产品或测试人员提供)、maven项目骨架建立标准化测试工程、dubbo/hessian/restful/webservice等接口协议的具体实现等限于篇幅,不做具体阐述。
然而,接口测试确实不是一个新话题。 在论坛上看到很多人介绍各种接口测试的实现方法。 我也从他们身上学习和参考了很多,所以这篇介绍就先这样结束吧。
,希望能给大家提供一些有用的参考。
主帖直接转到: