两,三年以前给大家分享过如何用jmeter做接口测试,但工具毕竟有限制,所以在真实的项目中,大家还是用自研的框架多。我之前写过一个简单的基于unittest+request的接口测试框架,也分享给大家过,最近在免费直播中我也有讲到,但直播毕竟讲不透彻,还是有很多同学不是特别清楚,到底如何做一个接口测试框架,今天我们再次详细解释下,如何生成自己的接口测试框架。
关于接口测试,基本概念请点击这里,接口测试我们一般先手工做,然后再自动化,无论手工还是自动化,接口测试的步骤都大致如下:
- 根据接口文档/规范及接口功能来设计测试用例,设计法则参考黑盒测试和白盒测试方法。
- 准备测试数据,用工具(Postman, fiddler, soapui等)或代码(python+requests或其它语言),根据测试用例构造请求,发请求。
- 检查服务器返回的结果。 包括状态码和返回值的检查。 各种合法非法的请求接口能否正确处理,要特别注意安全性(仅前端校验,后端忘记校验),authentication,性能(特别是并发),数据一致性,完整性方面(幂等)的问题。
接口测试的检查点,一般如下:
手工如何测试,很清楚了,那么我们讲接口测试自动化框架,从哪里开始呢?
既然是自动化,那么就必须不需要人工干预,框架如何做到不需要人工干预呢?窃以为有如下方面:
用例的自动收集
就是要自动接受用户输入,自动分析并查找待跑用例。什么意思呢?你跑自动化时候,一般需要指定跑那些case,这些case属于哪个类别(regression, smoke, unit test?)框架需要能根据用户输入快速找到要跑到用例集,并把它加到待跑用例的列表里。如果你不指定,框架会跑默认文件夹下的用例集。
用例的运行方式
就是组织查找到的用例集合,你想怎么运行? 顺序执行还是并发执行,执行过程中要不要记log,有错误是要继续还是要停止运行?运行失败要不要重新跑一遍?执行完毕后要不要收集执行结果?
测试报告 所有用例执行完毕后需要有整个运行情况的报告,包括整体运行结果,执行的用例列表,用例中成功百分比,失败百分比,失败的用例,框架有没有在它发生错误的时候截图?有没有记log,在失败的用例上点击用例名称,能不能通过链接的方式快速定位截图,log? 你想要txt格式的报告还是更user friendly的HTML格式?
前面3部分有了,一个测试框架的壳子就出来了,但还不够好,还需要加上:
自动触发运行
何时运行这个框架?每次有代码改动?发版前?还是每日定时?
环境配置
基本上公司的测试环境不可能只有一个,那么如何配置同样的脚本跑在不同的环境上?
Data provider (数据生成)
环境不一样,测试数据不能一样吧?如何提供不同环境的数据且不更改自动化代码?
邮件发送
自动化测试结束后自动发送测试报告到相应人的邮箱。
日志,错误处理
运行中记录运行情况,错误情况及出错后的处理。
下面,我们就以我实现的EasyAPIFramework(Python+Unittest+HTMLTestRunner)为例,详细说明下框架是如何一步步搭建起来的。
Unittest。unittest是python语言里使用最广泛的一个框架,看名字就知道它本来做unit test用的,但因为太强大了,所以也被拿来做功能自动化,接口自动化。
unittest有四个重要的概念:
1.test fixture
represents the preparation needed to perform one or more tests(setup, teardown)。
setUp(): 每次执行测试用例之前调用。无参数,无返回值。该方法抛出的异常都视为error,而不是测试不通过。没有默认的实现。
tearDown(): 每次执行测试用例之后调用。无参数,无返回值。测试方法抛出异常,该方法也正常调用,该方法抛出的异常都视为error,而不是测试不通过。只用setUp()调用成功,该方法才会被调用。没有默认的实现。通过setup 和 tesrDown组装一个module成为一个固定的测试装置。注意:如果setup运行抛出错误,则测试用例代码则不会执行。但是,如果setpu执行成功,不管测试用例是否执行成功都会执行teardown。
2.test case
独立测试的单位,一般一个testcase完成一个功能。注意两点:
(1).testcase通常继承自测试类,测试类一般继承自unittest.TestCase。测试类里通常实现了setup(), teardown(), 及测试用例。
(2).test case要以test开头。
3.test suite
A test suite is a collection of test cases, test suite。通常用来聚合测试用例, 并且test suites可以嵌套。
4.test runner
A test runner is a component which orchestrates the execution of tests and provides the outcome to the user.
运行测试用例的驱动类,可以执行TestCase,也可执行TestSuite。执行后TestCase和Testsuite会自动管理TestResult。简单来说就是run(),里面就是手工测试的步骤代码化。
除了上述概念,unittest里还有几个概念需要你知道:
5.TestLoder:是用来加载 TestCase到TestSuite中,其中有几个loadTestsFrom_()方法,
就是从各个地方寻找TestCase,创建他们的实例,然后add到TestSuite中,再返回一个TestSuite实例
6.TextTestRunner:是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法。
7.TextTestResult:测试结果会保存到TextTestResult实例中,包括运行了多少用例,成功与失败多少等信息
一般来说,unittest执行测试的流程如下:
创建好TestCase,然后由TestLoader加载(也可以用TestDiscover指定文件夹的方式Load)TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,整个过程集成在unittest.main模块中。
下面我们举个例子来看下,一个简单的unittest的测试用例长什么样子:
好,看上图,这里我实现了一个测试类,它继承了unittest.TestCase.然后再测试类里实现了setup(), test_XXX(), teardown()方法,有的测试方法上我加个了@unittest.skip(). 说这个什么意思呢?我开始说测试框架要实现查找测试用例集,unittest帮我们把测试用例标记好了,所有的测试用例要以test开头,当你调用TestLoder(defaultTestLoader()类,通过该类下面的discover()方法可自动根据测试目录start_dir匹配查找测试用例文件(test*.py),并将查找到的测试用例组装到测试套件)查找测试用例集的时候,所以test开头的会被自动加入测试用例集。哪些被标记为unittest.skip()的则不会。 第2,我想实现跑某些用例,怎么办呢?unitest 不支持按照标签运行,但是它提供了testsuite概念,你可以把一个测试类的几个测试用例添加到testsuite里。这样unitest就实现了框架的第一要素,测试用例集的查找。
那么用例集查找好了,你定义的test_XXX()的方法最终都会在TextTestRunner的run()方法中被执行到。也就是说,通过TextTestRunner我们实现了用例的顺序执行。(并发执行unittest貌似不支持,并发执行可以用pytest)看,利用unittest我们可以轻易开发出一个测试用例并实现了用例收集和用例执行。
那么测试报告如何生成呢?我们可以借助HTMLTestRunner来实现。
HTMLTestRunner is an extension to the Python standard library’s unittest module. It generates easy to use HTML test reports
下载地址:(http://tungwaiyip.info/software/HTMLTestRunner.html), 下载好后放到放在C:\Python37\Lib目录下。具体用法参考我代码,稍后放出。
好, 有了unittest +HTMLRunner,测试框架所需要的123就有了,后面的6到8(自动触发运行,环境配置,Data provider (数据生成),邮件发送,日志,错误处理),其中(自动触发运行,环境配置,邮件发送)可以通过集成Jenkins的方式实现, Data provider (数据生成),日志,错误处理,需要我们代码实现。
我们再回过来看这个简单的框架
这个框架分了几个部分(以前写的英文版我就不翻译了哈):
“Common”: –Common methods to facilitates the whole project.
html_report: To generate HTML report after test cases run.
shared_api: Import method like post/get to serve all the API requests.
txt_report: Independent way to run all the cases under test and generate simple txt format report.
主要来说就是一些公用的library,包括如对selenium的二次封装(web自动化),对API调用的二次封装(API自动化),datadriven读文件方法及项目所用到的公用库文件。
shared_api里包含了我对requests的封装,其实,针对webservices(一般为?wsdl文件)的API,也可以写个公用方法包括在内,可以看看它长什么样子:
如果你要做web自动化框架,那么就写个Selenium_helper,包括你所有对selenium的wrap方法,然后你在测试类的setup(), teardown()两个类下调用(看出来了吧,框架本身应该和什么类型的测试无关,框架就是用来帮你组织你的测试用例的)。
“Page”: –The page under test.
in this file, only store the page related function/method/date
test case related to this page should not be included here.
different page should have separated pages.
真正你要测试的项目(如果是功能自动化,你需要利用page object模式实现页面元素,和定位元素的loactor分离(其实测试数据,逻辑,业务都应该分离并可重用))。
一个完整的页面或功能我们组织在一起叫一个page,这个page应该包括这个页面的元素,及针对元素的操作,但测试业务逻辑不般不包括。
一个page通常是什么样子呢?
“Settings”: –Global settings & configuration & global data.
init:
data_source: all the account, common test data.
test config: environment, domain or other common config.
这里就是整个项目的配置文件,包括数据配置也可以放置在内,一般会有变量来接受来自jenkins的环境变量。如果没有就设置default值。 Test_config里应该包含你测试的数据,不同环境区分开,也可以写到外部excel或者xml了,然后利用common方法里的datadriven方法读出。 我们就是通过这个来实现环境及数据的切换。
“test” – Test cases inherited from pages, this is the cases under test.
each test file should have a page file accordingly.
framework will only search the cases under this folder
就是你测试类和测试方法所在,通常你测试类的名称要和你测试对象的名称一样,只是在前面加test(注意unittest只有以test开头的测试用例才会被执行)。
注意,setup()应该包括测试用例的数据准备(比如你需要打他driven,这次的数据就应该包括进来),还有selenium调用及browser的initial(针对web功能自动化),requests接口的初始化(API自动化)等。teardown()里,要做好相应的销毁动作。
“main” – Load & run all of the test cases defined in test folder.
System will automated search all the test method defined in test folder and run them.
当然你也可以通过TestDiscover及testsuite的addTest来筛选你需要执行的用例,我简化为搜索所有test文件夹下的用例了(直接利用discover方法)。
好了,到这里为止,我们就实现了一个基本的接口测试框架,是不是感觉非常简单啊?如果你对代码感兴趣,接口框架代码可以参考:
Github
其实,在真正的项目实践中,要考虑到比这个要复杂的多,起码并发执行我们没实现啊,数据驱动我们也没实现。失败rerun也没有。后续我会重新实现一个开源的接口测试框架给大家,把我提及的全部功能都实现,当然,大家也可以直接选用pytest,我后续也会写下pytest教程,敬请期待。