自研测试框架ktest介绍(适用于UI和API)
iTesting,爱测试,爱分享
在自动化测试的过程中,测试框架是我们绕不过去的一个工具,无论你是不需要写代码直接改动数据生成脚本,还是你需要检查测试结果甚至持续集成,测试框架都在发挥它的作用。
不同编程语言的实现出来的框架也不尽相同,但是思想总是相通的,比如尽量使框架使用者只关注自己的业务,框架帮助处理错误截图,保存错误log,出错重试甚至跟jenkins持续集成等。
可以说,一个还算合格的测试框架,可以大大提升测试效率;一个优秀的测试框架,说它能把测试人员从繁缛复杂的跟业务无关但又不得不做的工作中解脱出来也不为过。既然框架的作用这么明显,那么有哪些优秀的测试框架可以给我们用呢?
大家都知道, java里有TestNG, python里有unittest,pytest等优秀的“官方”框架, 我对python比较熟悉,之前也介绍过很多这方面的文章,相信大家也多少看过一两篇。
那么,为什么还要自己写一个框架呢?
相信常做自动化的都有自己的感悟, 比如自己业务没那么复杂,官方框架显得太重了;比如官方框架依赖很多的第三方库,每个要单独安装,更新,而且每个都有自己的用法,学习成本高;
痛点:
对于常用python写自动化的来说,unittest本身很优秀,但是不支持并发,而且测试报告支持的也不好,也不支持数据驱动。
解决方案:
基于上述痛点,大部分同学选择在原有开源框架上二次开发,比如unittest集合多线程threading.Thread实现并发,加入HTMLTestRunner实现报告展示,引入ddt实现数据驱动,然后美其名曰,我写了个框架,我每次听到都是先很崇拜,然后晚上暗搓搓拉下代码一看,MD,这不是就是搭了个积木取名要你命3000吗?你还不能说人家说自己写了一个框架不对,何况这个框架通常也很好用。
再有,开源的框架,毕竟普适多于贴合,跟自己的业务有时候就不那么紧密,为了使用某个具体功能还得引入很大一个包,也不是非常方便,另外最关键的一点是,我总觉得自己还行,想站起来试试 :)
框架是什么?我是怎么考虑框架的?
之前分享过几篇文章,
这些都是我工作的一些感悟,和对框架的一些思考,可以看到思想也是循序渐进的。你也可以看到不是“官方”框架,就是二次开发的“官方”框架。 当然也可以说我是常怀觊觎之心,不停研究的目的就是我个人非常想有一套完全自己实现的框架。
什么是ktest?
一句话:
ktest is a common test framework support for Both UI and API test with run in parallel ability。
跟其它的框架有什么不同?
参考了谁?
我当然不是闭门造车,参考了unittest,pytest,ddt,还有自己公司的官方框架, 读了部分源码,研究了下部分功能实现原理。
实现的功能:
1.多线程并发。(整套框架代码没出现任何哪怕一句threading,实现了并发,神奇不,嘿嘿)
详细介绍
先不介绍技术细节, 先把自己放在一个业务测试,或者刚接触自动化脚本的测试角色上,我拿到了一个测试框架,我最先想到的是什么? 如何用对吧? 用这个框架,我原有的测试用例需要做哪些改变?这个框架有哪些方便?你对框架的期待有哪些?
下面就详细介绍:
安装:
1#目前部署在自己公司的pypi库上,后期会上传到pypi。
2pip install ktest --index-url http://jenkin.xxxx.com:8081/pypi --trusted-host jenkins.xxxx.com
3#安装好后,你可以在 Python安装目录下的如下文件找到安装的包\Lib\site-packages
你的项目应该包括哪些:
在讲用法前,我们先来直观看下,你的项目目应该是什么样子的
你的项目中应该包含:
1.pages package, 这里面放你所有待测试页面,每个页面作为一个page object来保存。
可以看到,你只需要把精力放在你本身的业务上就好了。
ktest框架组成
package建立好了,我的测试用例,及我的待测页面要如何组织才能接入框架呢? 别急,我们先来看看框架本身长什么样子。
功能列表
看用红框标记起来的部分:
Common – 框架精华
1.欢迎关注公众号iTesting,跟万人测试团一起成长。
utiilites -- 拿来即用测试套件
这里面放一些半成品,比如连接数据库脚本,比如用excel做数据驱动,对excel进行读写的脚本。 用户不需要操心连接的建立,销毁等。
main.py 用来运行框架,并发运行控制也是在这里。并发我“舍弃”了threading.Thread, 代码量下降一半以上,且不用操心锁。
setup.py 用来把框架打包。
集成你的项目
1#bai_du.py
2#coding=utf-8
3__author__ = 'iTesting'
4import time
5from common.abstract_base_page import AbstractBasePage
6from page_objects import page_element
7class BAI_DU(AbstractBasePage):
8 BAI_DU_HOME = "http://www.baidu.com"
9 SEARCH_DIALOG_ID = "kw"
10 SEARCH_ICON_ID = "su"
11 MEMBER_COUNT_XPATH = "//option[@value='10']"
12 SAVE_XPATH = ".//*[@id='save']"
13 search_input_dialog = page_element(id_=SEARCH_DIALOG_ID)
14 search_icon = page_element(id_=SEARCH_ICON_ID)
15 member_count = page_element(xpath=MEMBER_COUNT_XPATH)
16 save_button = page_element(xpath=SAVE_XPATH)
17 def __init__(self, browser):
18 self.browser = browser
19 AbstractBasePage.__init__(self, browser)
20 def is_target_page(self):
21 self.browser.get(self.BAI_DU_HOME)
22 print(self.is_element_displayed_on_page(self.search_icon))
23 return self.is_element_displayed_on_page(self.search_icon)
24 def search(self, search_string):
25 self.search_input_dialog.send_keys(search_string)
26 self.search_icon.click()
27 time.sleep(5)
28 def preferences(self):
29 browser = self.browser
30 browser.get(self.BAI_DU_HOME + "/gaoji/preferences.html")
31 self.member_count.click()
32 time.sleep(1)
33 self.save_button.click()
34 time.sleep(1)
35 browser.switch_to_alert().accept()
每一个你的测试类(待测页面)你需要:
你的tests package下测试用例定义
1# test_baidu.py
2#coding=utf-8
3from common.selenium_helper import SeleniumHelper
4from common.test_case_finder import data_provider
5from common.test_decorator import TestClass, SetUpClass, Test, TearDownClass
6from pages.baidu import BAI_DU
7@TestClass(group='smoke', enabled=True)
8class TestBaidu:
9 test_case_id = '3'
10 @SetUpClass()
11 def before(self):
12 pass
13 def setUp(self):
14 self.browser = SeleniumHelper.open_browser('firefox')
15 self.baidu = BAI_DU(self.browser)
16 @data_provider([('baidu',), ('google',)])
17 @Test()
18 def test_baidu_search(self, x):
19 self.tag= 'tt'
20 """Test search"""
21 self.baidu.search(x)
22 assert 1== 1
23 @Test()
24 def test_baidu_set(self):
25 """Test set preference"""
26 print('NONONO')
27 self.baidu.preferences()
28 assert 1==0
29 def tearDown(self):
30 self.browser.delete_all_cookies()
31 self.browser.close()
32 @TearDownClass()
33 def after(self):
34 pass
注意事项
2.tags: 是每个用例的tag,定义在类属性里,test_finder查找时会解析这个tags,可以是str或者list或者tuple,或者是3者的嵌套。 框架最终会解析成一个list。
3.@SetUpClass(), @TearDownClass() 测试类装饰器,无输入参数。 每个测试类,不管它有多少个测试用例,这两个装饰器装饰的函数只会被执行一次。 一般用作测试类公用的数据的初始化,比如说,连接db查找某些值。 请注意, 并发运行,不要在这个函数里初始化你的browser,会有共享问题。
4.@TestClass(), 测试类的装饰类, 函数接受两个个参数一个是group,就是测试类所属的group,一个是enabled,默认值True。 值为False时, test_finder会把这个测试类略过。
5.@Test(), 测试装饰类, 函数接受一个参数enabled,默认值True。 值为False时, test_finder会把这个测试函数略过。
6.@data_provider(), 数据驱动装饰器。 接受一个参数,且此参数必须要iterable. 因为是数据驱动,不太可能只有一个数据,所以这个iterable,我通常我会定义成一个tuple,如果 有多个就是多个tuple, 例如[(1,2,3),(4,5,6)]这种,(1,2,3)会被解析成一条测试数据, (4,5,6)会被解析成另外一条。 这个概念来自ddt,我之前也介绍过相关框架。
7.@setUP(), @tearDown().两个函数,每个测试类必须定义,否则运行时框架会报错。 用作每个测试类的测试函数即每一条测试用例的运行前初始化和运行后的清理。
测试函数,就是以@Test()装饰的函数,一般是你的业务代码,你需要自己实现业务流程的操作和断言。如果用到setUpClass或者setUp里的方法属性,你只需要在这些属性前加self.
以上基本参照了pytest和unittest的用法,主要初衷也是为了减少迁移成本。
好,我测试类,测试函数都写好了,如何跑呢?
可用参数
1#最简单在命令行里输入ktest 即可, 框架会自动查询所有你项目文件下tests文件夹的测试用例。
2#ktest还支持如下参数:
3usage: ktest [-h]
4 [-t test targets, should be either folder or .py file, this should be the root folder of your test cases or .py file of your test classes.]
5 [-i user provided tags, string only, separate by comma without an spacing among all tags. if any user provided tags are defined in test class, the test class will be considered to run. | -ai user provided tags, string only, separate by comma without an spacing among all tags. only all of user provided tags are defined in test class, the test class will be considered to run.]
6 [-e user provided tags, string only, separate by comma without an spacing among all tags,if any user provided tags are defined in test class, the test class will be Excluded. | -ae user provided tags, string only, separate by comma without an spacing among all tags. all the provided tags must defined in test class as well.]
7 [-I include groups, string only, separated by comma for each tag. if any user provided groups are defined in decorator on top of test class, the test class will be considered to run. | -AI exclude groups, string only, separated by comma for each tag. all user provided groups are defined in decorator on top of test class, the test class which match will be excluded to run.]
8 [-E exclude groups, defined in decorator on top of test class, string only, separated by comma for each tag. If any user provided groups are defined in decorator on top of test class, the test class will be Excluded to run | -AE exclude groups, defined in decorator on top of test class, string only, separated by comma for each tag. All user provided groups must defined in decorator on top of test class. the test class which matched will be Excluded to run]
9 [-n int number] [-r dir]
其中:
1-t 是你要运行的测试目标的根目录,默认是项目下的tests文件夹。
2-i 是测试类里定义的tags。 tags会被解析成list,用户指定的任何tag只要包含在这个lists里,并且这个测试函数所属的TestClass()是enabled和这个测试函数的enabled是True,就表示这个测试类的这个测试函数会被test_filder找到。
3-I 是装饰测试类的@TestClass()定义的group,包含两个参数, 符合用户指定的group并enabled, 那么它装饰的类会被当作一个测试类被test_finder找到。
4-e 是测试类里定义的tags。 tags会被解析成list。 用户指定的任何tags包含在list里,这个测试函数就会被test_finder忽略。
5-E 是测试类里定义的tags。 tags会被解析成list。 用户指定的任何tags包含在list里,这个测试函数就会被test_finder忽略。
6-n 并发执行的个数,默认是cpu_count。
7-r 错误重跑, 默认是True。重跑再错误,所有跟case相关的log和screenshot会被记录。
Note:
有的同学会问了,我希望跑同时包括test和regression在内这两个tags的用例呢? 谁提出的这个需求?我真想指着你的鼻子说:
1# 定义了tag和group的更加严格版。只有用户输入的参数全部包含在测试用例定义的tags里,这个测试用例才会被test_finder扫描到。
2 -ai user provided tags, string only, separate by comma without an spacing among all tags. only all of user provided tags are defined in test class, the test class will be considered to run.
3 Select test cases to run by tags, separated by comma,
4 no blank space among tag values. all user provided
5 tags must defined in test class as well. tags are
6 defined in test class with name <tags>. eg: tags =
7 ['smoke', 'regression']. tags value in test class can
8 be string, tuple or list.
9
10 -ae user provided tags, string only, separate by comma without an spacing among all tags. all the provided tags must defined in test class as well.
11 Exclude test cases by tags, separated by comma, no
12 blank space among tag values. all of the tags user
13 provided must in test class as well. tags are defined
14 in test class with name <tags>. eg: tags = ['smoke',
15 'regression']. tags value in test class can be string,
16 tuple or list.
17
18 -AI exclude groups, string only, separated by comma for each tag. all user provided groups are defined in decorator on top of test class, the test class which match will be excluded to run.
19 Select test cases belong to groups, separated by
20 comma, no blank space among group values. All user
21 provided group must defined in decorator on top of
22 test class, the test class which match will be
23 collected. groups are defined in decorator on top of
24 test class. eg: group= 'UI'.
25
26 -AE exclude groups, defined in decorator on top of test class, string only, separated by comma for each tag. All user provided groups must defined in decorator on top of test class. the test class which matched will be Excluded to run
27 Exclude test cases belong to groups, separated by
28 comma, no blank space among group values. All of
29 groups user provided must defined in decorator on top
30 of test class as well. groups are defined in decorator
31 on top of test class. eg: group= 'UI'.
事实上, 为避免用法繁琐及方便用户, -i 和-ai, -e和-ae, -I和-AI, -E和-AE 是两两互斥的, 你只能指定其中的一个。
测试报告
下面我们看下一个运行实例
1ktest -I group -n 10 -r True
2执行中console的输出:
执行中console的输出:
执行成功后报告的展示:
report会自动生成在你项目根目录下,以运行时时间戳为文件夹,每个测试用例一个子文件夹。
测试报告加入了run pass, run fail, run error的图表。 run fail代表真正的fail, run error代表代码有问题或者环境问题导致的错误。同样报告直接按照测试类filter。
后记:
到此为止,ktest基本成型,也能根据需求完成web UI自动化和API自动化的工作了,不同无非是你在setUP初始化你的driver时候初始化的是你的browser还是request.session. 如果你想实现分布式并发,也可以在setUP initial selenium Grid, 前提是配置好selenium-server。
还是有一些感悟:
1.框架真不是一蹴而就的,是逐渐演化的。 最后成型的这一版跟我初始的规划还是有很大差距,有些代码甚至是不得已的妥协,比如我要出html报告,就要很多测试函数无关的数据收集,那么这些数据势必会侵入我的代码,结果就是我返回的测试函数数据结构很不简洁。
彩蛋:
放部分代码段:
test_finder部分代码:
测试类装饰器代码
关于更多技术实现细节,我会重新写一篇文章介绍。更多测试框架技术分享,请往下拉。
- End -
作者:
Kevin Cai, 江湖人称蔡老师。
两性情感专家,非著名测试开发。
技术路线的坚定支持者,始终相信Nobody can be somebody。
· 猜你喜欢的文章 ·
自研测试框架ktest介绍(适用于UI和API)的更多相关文章
- [原创]移动安全测试框架MobSF介绍
[原创]移动安全测试框架MobSF介绍 1 mobsf简介 Mobile Security Framework (移动安全框架) 是一款智能.集成型.一体化的开源移动应用(Android/iOS)自动 ...
- python nose测试框架全面介绍十---用例的跳过
又来写nose了,这次主要介绍nose中的用例跳过应用,之前也有介绍,见python nose测试框架全面介绍四,但介绍的不详细.下面详细解析下 nose自带的SkipTest 先看看nose自带的S ...
- python nose测试框架全面介绍七--日志相关
引: 之前使用nose框架时,一直使用--logging-config的log文件来生成日志,具体的log配置可见之前python nose测试框架全面介绍四. 但使用一段时间后,发出一个问题,生成的 ...
- python nose测试框架全面介绍六--框架函数别名
之前python nose测试框架全面介绍二中介绍了nose框架的基本构成,但在实际应该中我们也会到setup_function等一系列的名字,查看管网后,我们罗列下nose框架中函数的别名 1.pa ...
- python nose测试框架全面介绍五--attr介绍
之前写了一系列nose框架的,这篇介绍下attr tag 在nose框架中attr用来标识用例,使得在运行时可以通过标识来执行用例,之前在nose测试框架全面介绍四中有说明,但没有说明清楚,这里再总结 ...
- 爬虫、网页测试 及 java servlet 测试框架等介绍
scrapy 抓取网页并存入 mongodb的完整示例: https://github.com/rmax/scrapy-redis https://github.com/geekan/scrapy-e ...
- python nose测试框架全面介绍三
三.nose的测试工具集 nose.tools模块提供了一系列的小工具,包括测试执行时间.异常输出及unittest框架中所有的assert功能. 为了使写用例更加容易,nose.tools提供了部分 ...
- python nose测试框架全面介绍一
一.简介 nose 是python自带框架unttest的扩展,使测试更简单高效:nose是一个开源的项目,可以在官网上下载源码 1.快速安装 有以下几中安装方式: easy_install ...
- python nose测试框架全面介绍十二 ----用例执行顺序打乱
在实际执行自动化测试时,发现我们的用例在使用同一个资源的操作时,用例的执行顺序对测试结果有影响,在手工测试时是完全没法覆盖的. 但每一次都是按用例名字来执行,怎么打乱来执行的. 在网上看到一个有意思的 ...
随机推荐
- MySQL--启动和关闭MySQL服务
1.Windows下 启动服务 mysqld --console 或 net start mysql 关闭服务 mysqladmin -uroot shudown 或 net stop mysql 2 ...
- NOIp2018RP++
NOIp2018RP++ Rp=0 while True: Rp+=1; print (Rp)
- JS导出、导入EXCEL(案例)
插件下载地址:http://oss.sheetjs.com/js-xlsx/xlsx.full.min.js 1.导出excel <!DOCTYPE html> <html> ...
- 剑指offer【09】- 跳台阶
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). 对于本题,前提只有 一次 1阶或者2阶的跳法. a.如果两种跳法,1阶或者 ...
- CkEditor - Custom CSS自定义样式
CkEditor是目前世界上最多人用的富文本编辑器.遇上客户提需求,要改一下编辑器的样式,那就是深入CkEditor的底层来修改源码. 修改完的样式是这样,黑边,蓝底,迷之美学.这就是男人自信的表现, ...
- 使用Spring Boot和OAuth构建安全的SPA
最近一段时间都在闭关学习,过程还是有点艰辛的,幸运的是还有优锐课老师带着,少走了很多弯路.很久也没有更新文章了,这篇想和大家分享的是,了解如何在使用Spring Boot入门程序的同时使用Spring ...
- CodeForces 992B Nastya Studies Informatics + Hankson的趣味题(gcd、lcm)
http://codeforces.com/problemset/problem/992/B 题意: 给你区间[l,r]和x,y 问你区间中有多少个数对 (a,b) 使得 gcd(a,b)=x lc ...
- B-Tree(B树)原理及C++代码实现
B树是一种平衡搜索树,它可以看做是2-3Tree和2-3-4Tree的一种推广.CLRS上介绍了B树目前主要针对磁盘等直接存取的辅存设备,许多数据库系统也利用B树或B树的变种来存储信息. 本文主要针对 ...
- druid+mybaits简单集成
在前面的文章中,我们对springboot开发中一些常用的框架进行了集成,但是发现还是存在一些问题,比如druid还需要比较长的固有配置,实际上druid官方是提供了相关的starters包的,内部采 ...
- c/c++[001]:start
作为一个学校课程跳过c语言的萌新,这次重新学习c/c++从源头上明白这两种不同的输入输出还是很有必要 scanf()是C语言中的一个输入函数.与printf函数一样,都被声明在头文件stdio.h里, ...