自动化测试,也叫软件测试自动化。要学习软件测试自动化,首先就需要清楚什么是软件测试。
因为当局者迷,旁观者清的道理,软件开发是个复杂而周期性的过程,期间很容易产生或遗留下错误,而对于开发人员自己所编写与开发的应用程序(软件),往往有很多问题是他们自己发现不了,所以如果直接把存在不足的、有错误、有漏洞的应用程序直接运营上线提供给用户使用,那么很可能会给企业带来商业风险或影响企业受益,所以就需要软件测试人员进行软件测试了。
而软件测试(SoftwareTesting)就是为了尽快尽早地发现软件的各种软件缺陷而展开的贯穿整个软件生命周期、对软件(包括阶段性产品)进行验证和确认的活动过程。这个过程是在规定的条件下对程序进行测试操作并对其是否能满足设计要求进行评估,以达到发现、纠正程序错误,衡量和提升软件质量的目的。通俗点说,软件测试就是通过各种各样的手段或工具来尽可能的找到软件的不足和错误。
软件测试只能查找出软件中的错误或不足,但不能证明程序中没有错误,而且软件测试不能完全消灭软件的错误,只能尽早尽量多的发现软件中的错误与不足。
软件测试从不同的角度有着不同的分类方式。
在实际开发中,往往我们都是根据实际情况采用多种不同的测试手段、测试方式来对软件测试测试的。
软件缺陷,通常又被叫做bug或者defect,即为软件或程序中存在的某种破坏正常运行能力的问题、错误,其存在的最终表现为用户所需要的功能没有完全实现,不能满足或不能全部满足用户的需求。
bug出现的原因一般有如下几种情况,也就是说符合以下情况的问题都属于bug:
在整个bug处理的流程上,一般会把bug划分成多个不同状态。
缺陷报告,也叫bug报告,是软件测试人员重要的产出物之一,也是主要工作之一。一份高质量的缺陷报告可以帮助开发人员快速定位问题,修复Bug;也便于测试人员对缺陷进行统计、分析和跟踪管理,是测试人员和开发人员重要的沟通工具。开发中针对需求,测试bug,最怕的就是口口相传。
参考模板:
缺陷报告就是软件测试的结果产出物,而如何验证和测试缺陷?那就要继续往下学习更多内容了。
著名的敏捷开发布道师MikeCohn(迈克·科恩)在他的着作《SucceedingwithAgile》(中文名:《Scrum敏捷软件开发》)一书中提出了测试金字塔的概念。
根据MikeCohn的测试金字塔,测试的组合应该至少由以下三层组成(自下往上分别是):
意思是,应该把测试不同粒度的测试分布到整个软件不同层次中,而随着层次越高,编写的测试内容应该越少,也就是写许多小而快的低层次单元测试,适当写一些更粗粒度的中层次接口测试或集成测试,写很少的高层次UI测试、系统测试或验收测试。
所以,根据测试金字塔理论,接下来我们按部就班对测试自动化的内容进行学习。
禅道项目管理软件(简称:禅道)集产品管理、项目管理、质量管理、文档管理、组织管理和事务管理于一体,是一款功能完备的项目管理软件,完美地覆盖了项目管理的核心流程。
禅道的主要管理思想基于国际流行的敏捷项目管理方式—Scrum。Scrum是一种注重实效的敏捷项目管理方式,它规定了核心的管理框架,但具体的细节还需要团队自行扩充。禅道在遵循其管理方式基础上,又融入了国内研发现状的很多需求,比如bug管理,测试用例管理,发布管理,文档管理等。因此禅道不仅仅是一款scrum敏捷项目管理工具,更是一款完备的项目管理软件。基于scrum,又不局限于scrum。
禅道最大的特色是创造性的将产品、项目、测试这三者的概念明确分开,互相配合,又互相制约。通过需求、任务、bug来进行交相互动,最终通过项目拿到合格的产品。
目前,禅道和JIRA用的人较多。我们这里以禅道为例。
禅道项目管理软件是做什么的?
为什么用禅道这个名字?
禅和道这两个字含义极其丰富,有宗教方面的含义,也有文化层面的含义。禅道项目管理软件取其文化含义,期望通过这两个字来传达我们对管理的理解和思考。这个名字是受《编程之道》和《编程之禅》这两本书的启发。英文里面的禅为Zen,道为Tao,所以我们软件的英文名字为zentao。
我们可以在Windows,Mac,linux平台去搭建禅道。
如果是Windows平台,安装目录一定是磁盘的根目录
Windows平台的默认账号密码:
账号:admin密码:123456docker部署禅道[root@C/]#mkdir-p/docker_data/zento_data2.拉取镜像
[root@C~]#dockerpullidoop/zentao:12.0.1dockerrun-d-p6003:80--restart=always-eADMINER_USER="root"-eADMINER_PASSWD="password"-eBIND_ADDRESS="false"-v/docker_data/zentao_data:/opt/zbox/--add-hostsmtp.exmail.qq.com:163.177.90.125--namezentao-serveridoop/zentao:12.0.1账号:admin密码:123456禅道使用流程创建角色角色:
为了方便,角色密码以都为root!1234。
组织--添加用户/批量添加用户
填写:
创建计划的好处有:
需求变更
要在变更流程之后,指定有谁评审,这里指定项目主管评审。
当需求变更通过后,该需求的状态变成激活状态。
项目立项一般都是开个立项会:
完事之后,一般由项目经理在禅道中建立项目。
PS:项目组成员在线下已经分配好了,但还需要在项目创建后,手动的关联,所以,称这个过程为创建项目和创建团队。
编写项目信息:
当项目创建后,需要设置项目团队(开发组/测试组),进行项目与产品及产品计划进行关联。注意,关联操作会自动的关联在产品计划中已激活的需求。也可以手动的关联需求。
开发在任务列表中,能看到指派给自己的任务都有哪些,并且任务的时长。
开发在开发该任务时,要每天填写工时,能及时查看任务进度。
当该任务已经完成后,即剩余工时为0时,可以选择结束该任务,那此时的任务就处于已完成的状态。
当所有的任务都完成后,并且开发自测通过后,就可以进行提测,在提测前,要进行构建版本的过程。
编写版本信息。
在版本信息的描述中,要注意:
版本构建成功后,如下图
该版本实现了哪些需求
默认的,开发人员无权做给版本关联需求的操作,该权限只有研发主管、项目经理有权限。
如何给开发赋予该权限:
注意,该操作适用于为所有的角色赋予指定权限。
勾(多)选需求,然后点击关联需求按钮。
关联需求成功后,在版本详情中,能看到关联的需求:
此时的项目,在当前产品计划中,开发阶段基本完成,可以向测试提测。
编写提测单:
编写测试用例。
创建成功:
测试视图---测试单---点击关联用例
勾选用例并点击保存。
关联成功:
注意,只有用例的状态是正常的,才能被关联。
一般的,我们写的测试用例都是需要评审的,但是默认的,禅道并没有开启用例评审权限。
需要评审的用例的状态是待评审状态。
注意,测试人员也能评审自己的用例(也可以使用admin账号取消测试人员的评审测试用例的权限),但一般选择测试主管来评审。
首先明确,用例执行是一个实际的操作过程,只是在禅道中记录这一过程,并且进行bug管理。
测试视图--用例列表----选择执行用例
编辑bug信息:
开发人员访问测试---bug,点击指派给我的,bug详情中,点击确认按钮。
当开发修复bug后,我们测试人员需要进行回归测试。
如果回归测试成功,选择关闭bug
如果回归测试失败,重新激活该bug,开发继续修复,我们在进行回归测试,直到回归测试成功,然后关闭该bug。
创建环境:
编辑当前环境的变量:
如何在请求中应用上环境中的变量?
使用{{变量名}}的方式使用环境中的变量值。
全局变量:作用于全局,postman中的任何地方
集合变量:变量只作用于集合中的接口
自定义环境变量:自己定义的环境,在该环境中创建的变量,只要你使用该环境,就能使用其中变量值。
如何创建全局变量
使用{{变量名}}来使用。
只作用于当前的集合中。
创建
作用于所有地方,使用{{$内置的变量名}}
常用的:
python中的断言,assert,断定结果是怎样的,否则就断言失败。
postman中,可用的模板?
//状态码断言pm.test("判断响应状态码是否是200",function(){pm.response.to.have.status(200);});//判断json中的keyvalue是否符合预期,如果你的key是嵌套结构,就点下去pm.test("Yourtestname",function(){varjsonData=pm.response.json();pm.expect(jsonData.args.k2).to.eql("v2");});//判断文本类型的响应文本中,是否包含指定的字段pm.test("判断响应体是否包含指定的字段",function(){pm.expect(pm.response.text()).to.include("百度一下");});集合公共断言创建集合公共断言。
可以将集合中的一些公共的断言写到集合配置中,在集合中的接口在执行时,会自动的应用上集合配置中的断言。
postman不会帮我们处理token,只能我们自己来完成:
简单来说,webservice是通过xml进行交互的web请求,本质上也是HTTP请求。
WebService也叫XMLWebServiceWebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术。是通过SOAP在Web上提供的软件服务,使用WSDL文件进行说明,并通过UDDI进行注册。
如何调试webservice接口
如上图,在启动集合的时候,有如下参数配置:
数据驱动这里:一般轮训次数会自动的根据你的数据行数来定。如果轮训次数大于数据行数,那么在剩余的轮训中,提取的数据就是数据文件的最后一行。
这个意思是新版本的nodejs要求的Windows系统在win8以上,你可以安装一个稍微低一点的nodejs,如12.x版本
打开tomcat的启动文件,一闪即逝,意思是你的Java环境配置的有问题,解决办法:
所谓的单元测试(UnitTest)是根据特定的输入数据,针对程序代码中的最小实体单元的输入输出的正确性进行验证测试的过程。所谓的最小实体单元就是组织项目代码的最基本代码结构:函数,类,模块等。在Python中比较知名的单元测试模块:
phpphpunitjavajavaunit参考内容:
所谓的测试用例(TestCase),就是执行测试的依据和记录,把测试应用程序的操作步骤用文档的形式描述出来的一份文档。文档的格式可以是Excel、markdown、html、xmind网页。
一份合格的测试用例有利于测试人员理清测试思路,确保需要测试的功能周全没有遗漏,方便测试工作的开展和评估测试工作量,同时还可以便于测试人员记录测试数据和测试工作进度,为后续的回归测试提供样本参考,提升测试效率以及后续测试工作的交接。
那么一份合格的测试用例长什么样子或有什么内容呢?
参考文档:
在实际工作中,因为缺陷报告与测试用例作用相似,因此有时候会合并一起或只选择其中一种。
那么在工作中,我们一般都应该编写测试用例或者应该怎么设计测试用例来完成我们的测试工作呢?实际上在工作中,测试人员都是基于测试用例的7种基本设计方法来设计与编写测试用例的:
一般在工作中,我们比较常用的是等价类划分法与判定表法。
等价类划分法就是按照测试要求,把具有共同特征的测试数据划分为2类:有效等价类和无效等价类,把测试数据进行分类以后设计测试用例。
使用等价类划分法,可以让我们设计的测试工作更加科学有依据,避免出现穷举测试的情况,减少测试用例的数量。
例如,注册功能中用户名的测试用例,如果功能需求中,要求用户名必须长度为3-11个长度的字符。
判定表是分析和表达多逻辑条件下执行不同操作的情况的工具。而软件测试中的判定表法,就是把输入数据的各种可能情况进行组合罗列成一个判断表格,以判断表来设计测试用例。
判定表的表结构一般有如下2种:横向判断表与纵向判定表。
横向判断表:
纵向判定表:
例子,测试一个功能是否能修改文件。
Unittest具备完整的测试结构,支持自动化测试的执行,对测试用例进行组织,并且提供了丰富的断言方法,还提供生成测试报告。
importunittestprint(dir(unittest))上面的代码中,我们就引入了Unittest模块,同时可以通过打印发现Unittest框架中内置了大量的工具成员。这些工具成员中除了以下5个以外,其他的都不怎么常用。
前面讲到TestCase就是提供给我们编写测试用例的测试代码的,那么怎么编写一个测试用例?需要4个步骤即可。
在实际工作中,我们肯定是在项目中进行测试代码的编写或单独编写一个测试项目,但是我们现在刚开始学习,所以我们可以先编写一个例子代码,对其进行测试,以达到学习的目的。
unittest_01_测试用例的编写.py,代码:
importunittest#被测试的代码单元defadd(x,y):returnx+yclassFuncTest(unittest.TestCase):"""测试用例"""deftest_01(self):print(add(10,20))deftest_02(self):print(add("hello","world"))#deftest_03(self):#print(add("hello",20))#因为pycharm本身内置了执行unittest的功能,所以不适用以下代码也能执行,但是终端下或者使用其他的代码编辑器时,则需要加上。if__name__=='__main__':unittest.main()测试套件-TestSuite前面我们将到测试套件,主要用于把多个测试用例类打包集成到一个测试集中一起执行。工作中,一个项目往往需要编写非常多的测试用例,而那么多的测试用例也不可能只编写在一个文件中,此时就需要使用测试套件了。2个步骤:
unittest_02_测试套件的基本使用.py,代码:
importunittestimportunittest_01_测试用例的编写asunittest_01suite=unittest.TestSuite()##1.添加测试用例方法#suite.addTest(unittest_01.FuncTest("test_01"))#suite.addTest(unittest_01.FuncTest("test_02"))##2.批量添加测试用例方法#test_data=(unittest_01.FuncTest("test_01"),unittest_01.FuncTest("test_02"))#suite.addTests(test_data)##3.添加测试用例类#suite.addTest(unittest.makeSuite(unittest_01.FuncTest))#4.批量添加测试用例类test_data=(unittest.makeSuite(unittest_01.FuncTest),unittest.makeSuite(unittest_01.FuncTest))suite.addTests(test_data)TestSuite的作用仅仅是把多个测试用例打包集成到一块,但是并没有提供批量执行测试用例的方法,所以我们需要使用TextTestRunner了。
前面说过,TextTestRunner是用于执行测试用例、测试套件和输出测试结果的。2个步骤:
unittest_03_测试运行器基本使用.py,代码:
importunittestimportunittest_01_测试用例的编写asunittest_01suite=unittest.TestSuite()##添加测试用例方法#suite.addTest(unittest_01.FuncTest("test_01"))#suite.addTest(unittest_01.FuncTest("test_02"))##批量添加测试用例方法#test_data=(unittest_01.FuncTest("test_01"),unittest_01.FuncTest("test_02"))#suite.addTests(test_data)##添加测试用例类#suite.addTest(unittest.makeSuite(unittest_01.FuncTest))#批量添加测试用例类test_data=(unittest.makeSuite(unittest_01.FuncTest),unittest.makeSuite(unittest_01.FuncTest))suite.addTests(test_data)if__name__=='__main__':runner=unittest.TextTestRunner()runner.run(suite)测试加载器-TestLoader前面说过,用于加载测试用例TestCase,并生成测试套件TestSuite,实现自动从代码中加载大量测试用例到测试套件中。2个步骤:
发现其他目录中的脚本用例:
importunittestsuite=unittest.TestLoader().loadTestsFromModule(ff_case)suite=unittest.TestLoader().loadTestsFromName(name="ff_case.TestCase.test_case_01",module=ff_case.TestCase)suite=unittest.TestLoader().loadTestsFromNames(names=["ff_case.TestCase.test_case_01","ff_case.TestCase.test_case_02",],module=ff_case.TestCase)unittest_04_测试加载器基本使用.py,代码:
importunittestloader=unittest.TestLoader()#在当前目录下,搜索以unittest开头作为文件名的所有python文件,并把文件中的测试用例类打包集成到测试套件中suite=loader.discover("./",pattern="unittest*.py")if__name__=='__main__':runner=unittest.TextTestRunner()runner.run(suite)测试脚手架-TestFixture前面提到,测试脚手架会在执行一些测试代码之前与之后,让我们编写一些初始化和销毁的代码,主要分三个级别:
在测试用例类中提供了2个固定名字的实例方法(setUp与tearDown),用于完成方法执行前与执行后的操作。
unittest_05_测试脚手架_方法级别的脚手架.py,代码:
importunittest#被测试的代码单元defadd(x,y):returnx+yclassAddTest(unittest.TestCase):"""测试用例"""defsetUp(self):print("每个方法执行前都会执行一遍setUp实例方法,用于完成通用的前置操作或初始化工作")deftearDown(self):print("每个方法执行后都会执行一遍tearDown实例方法,用于完成通用的后置操作或销毁工作")deftest_01(self):print(add(10,20))deftest_03(self):print(add("hello",20))#因为pycharm本身内置了执行unittest的功能,所以不适用以下代码也能执行,但是终端下或者使用其他的代码编辑器时,则需要加上。if__name__=='__main__':unittest.main()类级别的脚手架setUpClass与tearDownClass在测试用例类中提供了2个固定名字的类方法(setUpClass与tearDownClass),用于完成类执行前与执行后的操作。
unittest_06_测试脚手架_类级别的脚手架.py,代码:
importunittest#被测试的代码单元defadd(x,y):returnx+yclassAddTest(unittest.TestCase):"""测试用例"""@classmethoddefsetUpClass(cls):print("当前类执行前都会执行一遍setUpClass类方法,用于完成通用的前置操作或初始化工作")@classmethoddeftearDownClass(cls):print("当前类执行后都会执行一遍tearDownClass类方法,用于完成通用的后置操作或销毁工作")deftest_01(self):print(add(10,20))deftest_03(self):print(add("hello",20))#因为pycharm本身内置了执行unittest的功能,所以不适用以下代码也能执行,但是终端下或者使用其他的代码编辑器时,则需要加上。if__name__=='__main__':unittest.main()模块级别的脚手架setUpModule与tearDownModule在测试用例类中提供了2个固定名字的函数(setUpModule与tearDownModule),用于完成类执行前与执行后的操作。
unittest_07_测试脚手架_模块级别的脚手架.py,代码:
使用HTMLTestRunner模块可以直接生成HTML格式的报告。HTMLTestRunner是一个不再维护的第三方的模块,通过pip工具安装不了,只能下载后手动导入。
因为HTMLTestRunner是python2模块的,所以在python3.x以后需要做些修改才可以使用,修改后版本代码如下:
importunittestfromHTMLTestRunnerimportHTMLTestRunnerimportunittest_01_测试用例的编写asunittest_01suite=unittest.TestSuite()test_data=(unittest.makeSuite(unittest_01.FuncTest),unittest.makeSuite(unittest_01.FuncTest))suite.addTests(test_data)if__name__=='__main__':#生成html报告withopen("test_report.html","wb")asfile:runner=HTMLTestRunner(stream=file,title="单元测试的HTML格式报告",description="python单元测试报告",tester="墨落")runner.run(suite)断言断言(assertion)是一种在程序中的判断测试用例执行结果是否符合预期结果的方式,所以断言也被称之为“期望”。当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。
unittest中常用的断言方法(加粗为重要方法):
unittest_09_断言.py,代码:
importunittestdefadd(x,y):returnx+yclassAddTest(unittest.TestCase):deftest_01(self):res=add(1,2)#断言结果是否与预期内容相同#self.assertEqual(res,3,msg="断言失败!一般会错误的结果与原因")#self.assertEqual(res,2,msg="断言失败!一般会错误的结果与原因")self.assertIn(res,[1,2],msg="断言失败!一般会错误的结果与原因")if__name__=='__main__':unittest.main()用例执行成功用.表示,执行失败是F表示。unittest.main()会自动的找到当前模块的unittest.TestCase的子类,然后找该子类内部以test开头的用例名,完事去一一的执行它们。
@unittest.skip(reason)#跳过用例的描述@unittest.skipif(condition,reason)#跳过的条件,跳过的原因unittest_10_跳过.py,代码:
importunittestclassMyCase(unittest.TestCase):deftest_case_01(self):self.assertTrue(1)@unittest.skip(reason='无条件跳过')deftest_case_02(self):self.assertTrue("")@unittest.skipIf(condition=3<2,reason='有条件跳过')deftest_case_03(self):self.assertTrue(0)if__name__=='__main__':unittest.main(verbosity=2)在输出的窗口中,跳过的用例用s表示;断言成功.表示;断言失败F表示。
当需要使用多组不同的测试数据测试同一个方法时,可以使用unittest参数化来解决。常用的参数化方法有ddt、parameterized
pipinstallparameterizedunittest_11_参数化.py,代码:
importunittestfromparameterizedimportparameterizeddefadd(x,y):returnx+yversion=(2,7,0)classAddTest(unittest.TestCase):defsetUp(self):print("setUP执行....")@parameterized.expand([(10,20),("a","B"),(50,20)])deftest_00(self,x,y):res=add(x,y)self.assertIn(res,[1,30,"aB",70],msg="断言失败!一般会错误的结果与原因")#deftest_01(self):#res=add(1,2)#self.assertIn(res,[1,3],msg="断言失败!一般会错误的结果与原因")##deftest_02(self):#res=add("a","B")#self.assertEqual(res,"aB",msg="断言失败!一般会错误的结果与原因")##deftest_03(self):#print(add("a",20))if__name__=='__main__':unittest.main()数据驱动测试Data-DrivenTests(DDT)即数据驱动测试,可以实现多个数据对同一个方法进行测试,达到数据和测试代码分离,目的是为了减少测试用例的数量。
基本安装
pipinstallddt直接传递单个数据
unittest_12_参数化_基于ddt直接传递数据.py,代码:
importunittestfromddtimportddt,datadefadd(a,b):returna+b@ddtclassAddTest(unittest.TestCase):##单次传递一个数据到测试用例方法中#@data(100)#@data([1,2,3,4])#@data({"a":1,"b":2})#@data((1,2,3))##多次传递一个数据到测试用例方法中#@data(*["a","b","c"])#字符串#@data(*[{"a":1},{"a":2},{"a":3}])#字典#@data(*[[1,1,1],[1,1,2],[1,1,3]])@data([1,1,1],[1,1,2],[1,1,3])deftest_01(self,a):print(a)if__name__=='__main__':unittest.main()unittest_13_参数化-基于ddt解包传递多个数据.py,使用unpack装饰器解包数据
Pytest是基于Python语言的单元测试框架,也是一个命令行的工具,比unittest测试框架更灵活。具有以下特点:
安装
pipinstallpytest#测试是否安装成功pytest--version快速入门基本格式pytest_01_基本格式.py,代码:
defadd(x,y):returnx+yclassTestAddFunc(object):#测试用例类名必须用Test开头,若执行收集不到用例则将文件名改为test_开头试试deftest_01(self):#方法名与函数名必须要用test_开头print(add(10,20))deftest_02(self):print(add("a","B"))deftest_03(self):print(add("a",20))测试运行pytest提供了三种方式给测试人员执行测试用例:
类级别:setup_class与teardown_class,注意:这是实例方法,不是类方法
模块级别:setup_module与teardown_module
pytest_02_测试脚手架.py,代码:
defadd(x,y):returnx+ydefsetup_module():print("模块执行初始化操作")defteardown_module():print("模块执行初始化putest")classTestAddFunc(object):#测试用例类名必须用Test开头defsetup(self):print('setup执行初始化操作')defteardown(self):print('teardown执销毁操作')defsetup_class(self):#注意:此处方法类型是实例方法。print('类级别:setup_class执行初始化操作')defteardown_class(self):#注意:此处方法类型是实例方法。print('类级别:teardown_class执行初始化操作')deftest_01(self):#方法名与函数名必须要用test_开头print(add(10,20))deftest_02(self):print(add("a","B"))deftest_03(self):print(add(20,20))基于配置文件运行pytest在pytest提供的终端运行测试用例的方式的基础上,pytest还支持使用配置文件来简化运行参数。
可以通过pytest--help查看pytest配置文件的名:pytest.ini、tox.ini、setup.cfg。
配置文件一般保存在项目根目录下。
pytest.ini,配置文件格式:
;命名空间,表示以下选项属于pytest配置[pytest];运行参数addopts=-s-v;匹配搜索的测试文件的目录路径testpaths=./;匹配搜索的测试文件名格式python_files=test_*.py;匹配搜索的测试类格式python_classes=Test*;匹配搜索的测试方法名格式python_functions=test_*;markersmarkers="测试标记-一般就是本轮测试的项目名或者模块"上面的注释,必须清除否则报错。有了配置文件以后,使用pytest命令即可运行测试用例。
pytest断言Pytest的断言比unittest提供的断言更加简单易用,仅仅只需要使用assert关键字,后续跟上python原生的表达式即可。
assert"m"in"moluo"assert"m"notin"moluo"assert1==2assert1!=1assert1>2assertnotTrueasserttype(1)isintasserttype(1)notisintpytest_03_断言.py,代码:
defadd(x,y):returnx+yclassTestAddFunc(object):#测试用例类名必须用Test开头deftest_01(self):#方法名与函数名必须要用test_开头res=add(10,20)assertres==30deftest_02(self):res=add("a","B")asserttype(res)isintdeftest_03(self):res=add(20,20)assertres!=20跳过根据特定的条件,不执行标识的测试函数。
@pytest.mark.skipif(判断条件,reason="跳过原因")pytest_04_跳过.py,代码:
importpytestdefadd(x,y):returnx+yversion=(2,7,12)classTestAddFunc(object):#测试用例类名必须用Test开头deftest_01(self):#方法名与函数名必须要用test_开头res=add(10,20)assertres==30@pytest.mark.skipif(version<=(2,7,12),reason="高于2.7以下,不测试test_02")deftest_02(self):res=add("a","B")asserttype(res)isintdeftest_03(self):res=add(20,20)assertres!=20参数化pytest也支持参数化操作,而且不需要安装任何第三方模块即可使用,也不再需要ddt。
importpytestdefadd(x,y):returnx+yclassTestAddFunc(object):#测试用例类名必须用Test开头@pytest.mark.parametrize("x,y",[(10,20),{"x":10,"y":20},("a","b"),("a",20)])deftest_01(self,x,y):#方法名与函数名必须要用test_开头res=add(x,y)assertres==30进阶使用fixture-脚手架在Unittest中我们经常需要针对不同的测试用例使用脚手架完成一些测试的前置与后置操作,但是很多测试用例的前置与后置操作基本一样,所以pytest提供的fixture脚手架相比Unittest提供的脚手架进行了显著改进:
pytest的fixture有个scope参数可以控制fixture的作用范围(从大到小):session>module>class>function。
代码:
importpytest@pytest.fixture(scope="class",autouse=True)deffixture_open_browser():print("打开浏览器")#相当于setupyield"xiaoming","123456"#生成器函数中的暂停关键字,作用是当代码运行到yield时,把yield右边的数据作为返回值提供给调用处,把代码执行权交出去。print("关闭浏览器")#相当于teardownclassTestUser(object):#object是一个基类,python中所有的类都是默认继承于object的。deftest_01(self,fixture_open_browser):print(f"fixture_open_browser={fixture_open_browser}")print("注册流程,测试用户是否能注册成功")deftest_02(self,fixture_open_browser):print(f"fixture_open_browser={fixture_open_browser}")print("登陆流程,测试用户是否能登陆成功")单独存放fixture代码我们还可以基于fixture脚手架把代码提前写好,放在在一个pytest能自动识别的conftest.py文件中,这样可以有效避免出现重复的fixture代码。注意:conftest.py的文件名必须固定,而且里面只存放fixture代码,并保证该文件与被测试代码文件在同一目录即可。
conftest.py,代码:
importpytest@pytest.fixture(scope="class",autouse=True)deffixture_open_browser():print("打开浏览器")#相当于setupyield"xiaoming","123456"#生成器函数中的暂停关键字,作用是当代码运行到yield时,把yield右边的数据作为返回值提供给调用处,把代码执行权交出去。print("关闭浏览器")#相当于teardown5-单独存放fixture代码.py,代码:
classTestUser(object):deftest_01(self,fixture_open_browser):print(f"fixture_open_browser={fixture_open_browser}")print("注册流程,测试用户是否能注册成功")deftest_02(self,fixture_open_browser):print(f"fixture_open_browser={fixture_open_browser}")print("登陆流程,测试用户是否能登陆成功")第三方常用组件控制测试用例执行顺序unittest执行测试用例的默认顺序是根据测试用例方法名的ASCII码排序[0-9A-Za-z]而定的,值越小,越靠前执行。
pytest执行测试用例的默认顺序是根据测试方法的源代码上下顺序来排序的。
而如果在完成接口测试或集成测试时,我们要控制测试用例的执行顺序,可以通过pytest的第三方模块pytest-ordering来实现。
pipinstallpytest-ordering使用
classTestAdd(object):@pytest.mark.run(order=n)#n表示执行顺序,可以是正负整数。deftest_测试方法名(self):pass#执行顺序为优先执行正数排序的方法,接着到没有排序的方法,最后是负数排序的方法。#如果多个方法都是正数,则先执行排序值小的,同理如果多个方法都是负数,也是一样先执行排序值小的。pytest-ordering.py组件的使用,代码:
"""前置步骤,安装插件:pipinstallpytest-ordering"""importpytestclassTestAdd(object):@pytest.mark.run(order=-1)deftest_01(self):print(f"test_01执行了,order=-1")@pytest.mark.run(order=-10)deftest_02(self):print(f"test_02执行了,order=-10")@pytest.mark.run(order=10)deftest_03(self):print(f"test_03执行了,order=10")@pytest.mark.run(order=3)deftest_04(self):print(f"test_04执行了,order=3")deftest_05(self):print(f"test_05执行了,没有指定排序值")deftest_06(self):print(f"test_06执行了,没有指定排序值")"""多个方法排序值为正整数的情况:以小为先test_04test_03没有排序值的情况下,源代码中先写的先执行,后写的后执行:先写为先test_05test_06多个方法排序值为负整数的情况:以小为先test_02test_01"""pytest-ordering用于解决测试开发中,UI测试、系统测试、接口测试等多个测试用例有先后流程的场景。
例如,系统测试的购物车流程:
注意:pytest-ordering组件如果和参数化fixtrue脚手架一起使用会导致参数解析错误,所以不能一起使用。因此pytest-ordering使用时,如果需要对测试用例实现参数化,则可以使用pytest.mark.parametrize,注意:不能使用ddt。
针对网络场景或服务端性能不稳定的情况下,进行测试时经常遇到用例运行失败的情况,特别在性能测试方面,此时我们可以让失败用例重试指定次数,以达到测试的更准确的结果。
importrandomdefadd(x,y):returnx+yclassTestAdd(object):deftest_01(self):res=add(10,20)assertresis30deftest_02(self):ret=random.randint(1,3)assertret%2==0配置文件pytest.ini,代码:
[pytest]addopts=--reruns3--reruns-delay2-s-vtestpaths=./python_files=test_*.pypython_classes=Test*python_functions=test_*局部失败用例重试局部失败用例重试.py,代码:
importrandomimportpytestdefadd(x,y):returnx+yclassTestAdd(object):deftest_01(self):res=add(10,20)assertresis30#只设置当前测试用例方法失败重试@pytest.mark.flaky(reruns=3,reruns_delay=2)deftest_02(self):ret=random.randint(1,3)assertret%2==0注意:
pipinstallpytest-xdist使用
pytest-xdist安装以后,pytest会新增一个参数-n,可以让我们指定本次运行测试所开启的进程数量。
参数设置如下:
pytest-s-v-n4#使用4个进程运行,也可以改成autopytest-s-v-nauto#自动检测系统的CPU核数,并根据CPU核算创建对应数量的进程数量pytest.ini,代码:
[pytest]addopts=-s-v-nautotestpaths=./python_files=test_*.pypython_classes=Test*python_functions=test_*代码:
defadd(x,y):returnx+yclassTestAdd(object):deftest_01(self):res=add(10,20)assertresis30deftest_02(self):res=add("10","20")assertres=="1020"deftest_03(self):res=add("10","20")assertres=="1020"deftest_04(self):res=add("10","20")assertres=="1020"deftest_05(self):res=add("10","20")assertres=="1020"deftest_06(self):res=add("10","20")assertres=="1020"自身生成HTML格式测试报告[了解下即可]安装
pipinstallpytest-html使用
安装插件到本地以后,在pytest运行参数中会新增选项:--html=report.html生成HTML格式测试报告.py,代码
importrandomimportpytestdefadd(x,y):returnx+yclassTestAdd(object):deftest_01(self):res=add(10,20)assertresis30#只设置当前测试用例方法失败重试@pytest.mark.flaky(reruns=3,reruns_delay=2)deftest_02(self):ret=random.randint(1,3)assertret%2==0pytest.ini,代码:
[pytest]addopts=-s-v--html=report.htmltestpaths=./python_files=test_*.pypython_classes=Test*python_functions=test_*AllureAllure是一款轻量级的开源自动化测试报告生成框架,Java语言开发出来的。它支持绝大部分测试框架,比如pytest、unittest等。比起上面那些丑陋的测试报告生成,Allure是最漂亮的,而且还可以配合pytest与Jenkins实现CI持续集成。pytest+Allure+git+pycharm+Jenkins+gitlab/gitee/github=CI持续集成
allure插件安装:解压压缩包到一个没有中文的目录中,然后将其中的bin目录添加到环境变量(PATH)中
测试,终端输入:
Allure的运行起来需要依赖于java环境,此处下载安装jdk1.8环境(Java8)。
此处我们演示windows下的java环境安装。
鼠标右键点选下载到本地的java的jdk安装包,选择以"管理员身份运行",窗口如下,勾选窗口左下角的"更改目标文件夹",点击"安装",进入下一步。
默认情况下,java的jdk会选择在C盘的"ProgramFiles"目录下安装,如果不想要在此目录安装,则可以点击更改,如果无所谓,则点击"下一步"即可。建议修改“ProgramFiles”之前的路径,后半段保持原样。
jre与jdk同样保存在一个父目录下即可。
OK,接下来,喝杯茶,等待一会即可。
效果如下,则没有问题。
注意:javasdk环境是依赖于JAVA_HOME的,依次打开控制面板系统与安全系统高级系统设置环境变量系统变量新建。变量名中输入JAVA_HOME,变量值中填写刚才获取到的路径C:\tool\Java\jdk1.8.0_201(注意,此处根据自己的实际路径填写,别瞎复制)。
保存了JAVA_HOME环境变量,点击下方的Path环境变量,里面把javasdk安装目录下bin目录加入到环境变量中。
cmd终端输入java-version,出现如下内容表示安装完成。
接下来,只需要安装allure集成到pytest的pytest-allure集成模块即可。
pipinstallallure-pytest基本使用生成allure测试结果,结果以json文件格式保存在--alluredir选项指定的目录下。
pytest.ini,代码:
[pytest]addopts=-s-v--alluredir=./allure_resultstestpaths=./python_files=test_*.pypython_classes=Test*python_functions=test_*基于json报告结果使用浏览器展示allure测试结果报告
allureserve./allure_results生成HTML格式文档的测试报告
HTML格式文档的测试报告也是要基于上面的json文件格式的测试结果才能生成的。
#必须先生成json格式的测试报告,才能生成HTML格式的测试报告alluregenerate./allure_results-o./reports--clean基于json格式的报告生成HTML报告,代码:
pythonmain.py补充:关于在python中可以执行命令行命令方法一:
settings.py
如何删除一个非空目录
用shuitil
接口自动化的框架开发:
实现思路:
我们要构建一个自动化测试框架,就要以项目的概念来对项目中所有的代码文件进行划分目录和文件结构,不同的代码功能不一样,所以我们需要设计一个合理的目录结构,以方便与测试开发团队的其他人员进行测试功能的开发与测试,也方便将来的项目代码维护。
根目录/├─config.py#项目代码配置文件├─pytest.ini#pytest模块配置文件├─main.py#主程序,执行入口├─api/#封装被测试项目的api接口存放目录[用于mock测试、冒烟测试]├─data/#测试数据/测试用例的存放目录├─allure_results/#测试报告结果生成目录├─allure_reports/#HTML测试报告生成目录├─tests/#测试用例脚本存放目录├─libs/#第三方工具类的存放目录[开源模块,不是当前项目封装的模块]└─utils/#自定义工具类的存放目录[当前项目自己封装的模块]配置文件,config.py,代码:
importpathlib#路径操作模块,替代os.path模块,os.path采用字符串来操作路径,pathlib采用面向对象来操作路径#项目目录的主目录路径[字符串路徑]BASE_DIR_STR=pathlib.Path(__file__).parent.resolve().as_posix()#基本操作系统转换路径的分隔符as_posix#項目目录的主目录路径[路径对象]BASE_DIR=pathlib.Path(BASE_DIR_STR)#项目名WEB_NAME="路飞自动化接口测试框架"#测试自动化项目的运行端口与IP地址HOST="127.0.0.1"PORT=8088#print(pathlib.Path(__file__))#D:\data\1045699\Desktop\luffy_code\testting\luffytest\config.py#print(BASE_DIR_STR,type(BASE_DIR_STR))#D:/data/1045699/Desktop/luffy_code/testting/luffytest
[pytest]addopts=-s-v--alluredir=./resultstestpaths=./python_files=test_*.pypython_classes=Test*python_functions=test_*OK,完成了上面操作以后,我们就可以写一个测试用例来测试下现在我们的基本框架是否能正常运行了。
tests/users/test_login.py,代码:
注意:公司内部的代码不要私自自己往gitee(码云),github去推。
需要提前在当前开发机子上安装git代码版本管理工具。
我们是学习,所以我这创建项目库名luffytest。项目库名建议是英文的。
选择git-flow自定义分支模型。
所谓的分支,其实就是一个项目的代码的不同流程版本。
git-flow分支命名规范:
补充说明:
接下来,我们可以使用ssh连接远程的git仓库,需要先在本地电脑下生成ssh秘钥对。
复制终端下出现的公钥信息,复制到码云上面。
切换项目的仓库地址,设置线上仓库
gitconfig--globaluser.name"mooluo"gitconfig--globaluser.email"649641514@qq.com"#在项目根目录下初始化git仓库cdluffytest/#具体的路径根据自己的设置而定gitinit#gitremoteremoveorigin#删除仓库地址,origin可以理解是一个变量,因为当前时一个新仓库,所以不需要执行这段。gitremoteaddorigingit@gitee.com:mooluo_admin/luffytest.git#新仓库地址,等同于origin=git..../luffycity.git分支管理,git提交代码版本并同步到远程服务器。
gitbranch#查看分支#刚执行gitinit初始化时,会没有分支,因此我们需要进行第一次的代码提交。gitadd.gitcommit-m"feature:项目初始化"#经过上面的命令操作,本地的git就会自动生成一个master分支#gitbranch<分支名称>#新建分支#gitbranchtest#例如:创建一个test分支#gitcheckout<分支名称>#切换分支#gitcheckouttest#例如:切换到test分支,检出分支代码#gitbranch-d<分支名称>#删除分支#gitbranch-dtest#gitpush<远程仓库别名>--delete<分支名称>#删除远程服务器分支#gitpushorigin--deletetest#例如:删除远程仓库origin中的test#推送代码记录到远程服务器的代码仓库gitpushoriginmaster#推送的过程中,如果本地有该分支,但是线上没有这个分支,则git会自动在远程中创建该分支,默认的空仓库是一个分支都没有的。使用.gitignore可以在git上传或下载代码时,把一些不必要记录的垃圾文件/目录过滤掉。
注意:必须保证.git目录和.gitignore在同一级目录下,才能生效。
.gitignore文件内容:
gitadd.gitcommit-m"feature:新建.gitignore忽略文件"#推送代码记录到远程服务器的代码仓库gitpushoriginmastergitcommit提交版本的描述信息,编写前缀规范:
最终,成功提交了代码版本到gitee平台。
针对的项目开发,将来肯定需要把测试框架这个项目保存公司的服务器的,所以如果项目在公司服务器报错了,我们有可能不在场,或者其他同时去运作,那么我们针对当前这个项目在这个运行期间,有没有出现异常,那就需要记录整个项目的运行信息。
config.py,新增如下日志配置代码,代码:
importpathlib#路径操作模块,替代os.path模块,os.path采用字符串来操作路径,pathlib采用面向对象来操作路径#项目目录的主目录路径[字符串路徑]BASE_DIR_STR=pathlib.Path(__file__).parent.resolve().as_posix()#基本操作系统转换路径的分隔符as_posix#項目目录的主目录路径[路径对象]BASE_DIR=pathlib.Path(BASE_DIR_STR)#项目名WEB_NAME="路飞自动化接口测试框架-master"#测试自动化项目的运行端口与IP地址HOST="127.0.0.1"PORT=8088"""日志配置"""LOGGING={"name":"luffytest",#日志处理器的名称,一般使用当前项目名作为名称"filename":(BASE_DIR/"logs/luffytest.log").as_posix(),#日志文件存储路径,注意,一定要在项目根目录下手动创建logs目录"charset":"utf-8",#日志内容的编码格式"backup_count":31,#日志文件的备份数量"when":"d",#日志文件的创建间隔事件,m表示每分钟创建1个,h表示每小时创建1个,d表示每天创建1个,m0~m6表示每周星期日~星期六创建1个,midnight表示每日凌晨}在项目根目录下创建logs目录,并编写日志工具类,utils/logger.py,代码:
其中我们作为测试开发,比较常用的就是requests模块了。
Requests是一个Python编写,基于urllib的开源HTTP网络请求工具第三方库。它python内置的urllib模块使用更加简单便,可以节约我们量的作,完全满HTTP测试需求。
pipinstallrequestsrequests.request(method,url,**kwargs)类能够构造一个请求,支持不同的请求方式。
流式请求,指的不是请求是流,而是请求返回的数据流,返回一点取一点,而普通的请求是返回完毕你再取内容。
发送无参数的GET请求
demo/demo_requests.py,代码:
utils/requestor.py,代码:
pipinstallflaskpipinstallpymysqlpipinstallflask_sqlalchemy项目初始化api/__init__.py,代码:
importconfigfromflaskimportFlaskfromflask_sqlalchemyimportSQLAlchemy#SQLAlchemy初始化db=SQLAlchemy()app=Flask(__name__)definit_app():#加载配置app.config.from_object(config)#加载mysql数据库配置db.init_app(app)#db创建数据表withapp.app_context():db.create_all()returnappconfig.py,添加配置信息,代码:
createdatabasepytest;项目根目录下,单独创建run.py文件,启动mockserver,代码:
importconfigfromapiimportinit_app#注意,务必把模型models的内容以及views中的服务端接口引入当前文件,否则flask不识别。fromapiimportmodelsfromapiimportviewsapp=init_app()if__name__=='__main__':app.run(host=config.API_HOST,port=config.API_PORT)提交代码版本,代码:
gitadd.gitcommit-m"feature:基于flask实现mockserver"#推送代码记录到远程服务器的代码仓库gitpushoriginmaster编写测试用例测试mockservertests/users/test_login.py,代码:
gitadd.gitcommit-m"test:编写测试用例测试mockserver"#推送代码记录到远程服务器的代码仓库gitpushoriginmaster基于数据驱动生成用例代码在实际测试开发中,我们一般使用参数化来自动生成测试用例,前面介绍过常用的有ddt与parametrize。那么在pytest中,因为本身提供了parametrize参数化,所以我们往往会在unittest中采用ddt来实现参数化,而在pytest中采用内置的parametrize即可。
而参数化所需要的测试用例,一般我们也是可以采用json,yaml或Excel文件来存储,如果用例数量太多,还可以改成数据库保存。
YAML(递归缩写:YAMLAin'taMarkupLanguage,译作:YAML不是一种标记语言)是一种可读性高,用来表达数据序列化的数据格式,使用场景与xml、json类似,2001年首次发布,在最初开发YAML语言时YAML的意思其实是:"YetAnotherMarkupLanguage"(仍是一种标记语言)。后面之所以改了名称,原因是为了强调YAML语言以数据为中心,而不是以标记语言为重点。
1.大小写敏感2.使用缩进表示嵌套层级关系,且缩进不允许使用tab,只允许使用空格缩进(缩进的空格数不重要,只要相同层级的元素左对齐即可)3.'#'表示注释4.'~'表示空(None,null),也可以使用null表示None,但是尽量不要在属性中出现null或者~5.yaml文件中,属性唯一不能重复,否则报错6.文件扩展名为yml或yaml,如:data.yaml或者data.ymltest.yaml,代码:
username:'xiaoming'age:16数据类型类型名称对应python的数据类型描述纯量(scalars)整型、浮点型、布尔型、字符串、None单个的、不可再分的值数组(Array)列表、元祖一组按次序排列的值,又称为序列(sequence)/列表(list)对象(object)字典键值对的集合,又称为映射(mapping)/哈希(hashes)/字典(dictionary)纯量纯量是最基本的,不可再分的值。类似python中的字符串、布尔值、整型、浮点型、None等
pipinstallpyyaml基本使用
importyaml"""读取yaml文件的数据"""#withopen("./data.yaml","r",encoding="utf-8")asf:#content=f.read()#data=yaml.load(content,Loader=yaml.FullLoader)#print(data)#print(data["name"])"""把数据写入yaml文件"""fromdatetimeimportdatetimewithopen("./data2.yaml","w",encoding="utf-8")asf:#yaml.dump(data,f,Dumper=yaml.SafeDumper)#没有多字节内容的情况下data={"name":"xiaoming","age":17,"datetime":datetime.now(),"point":1.245464E10,"goods_list":[{"name":"xiaoming","age":17,"sex":True},{"name":"xiaoming","age":17,"sex":True},{"name":"xiaoming","age":17,"sex":True},{"name":"xiaoming","age":17,"sex":True},],"author_list":["小明","小白","小红"],"user_info":{"username":"小明","password":"123456"}}yaml.dump(data,f,Dumper=yaml.SafeDumper,allow_unicode=True)#有多字节内容的情况下,中文就是多字节内容封装yaml工具类,utils/yamler.py,代码:
importyamlfromloggerimportLogHandleclassYaml(object):"""yaml操作工具类"""__instance=Nonedef__new__(cls,*args,**kwargs):ifnotcls.__instance:print("创建Yaml的单例")cls.__instance=super(Yaml,cls).__new__(cls,*args,**kwargs)returncls.__instancedef__init__(self):self.logger=LogHandle().get_logger()defread(self,path):"""读取yaml文件"""withopen(path,encoding="utf-8")asf:result=f.read()ifresult:result=yaml.load(result,Loader=yaml.FullLoader)returnresultdefwrite(self,path,data):"""写入yaml文件"""try:withopen(path,"w",encoding="utf-8")asf:yaml.dump(data,f,Dumper=yaml.SafeDumper,allow_unicode=True)returnTrueexceptExceptionase:self.logger(f"写入数据到yaml文件失败:{e}")returnFalseif__name__=='__main__':ya=Yaml()data=ya.read("../demo/yaml_demo/data.yaml")print(data,type(data))提交代码版本,代码:
gitadd.gitcommit-m"feature:封装yaml工具类"#推送代码记录到远程服务器的代码仓库gitpushoriginmaster基于yaml数据驱动生成测试用例data/user_login.yaml,代码:
fromloggerimportLogHandlelogger=LogHandle().get_logger()defassertor(assert_list,response):"""断言函数"""iftype(assert_list)isnotlist:assert_list=[assert_list]forexprinassert_list:logger.info(f"开始断言:assert{expr}")ifexpr:exec(f"assert{expr}",{"code":response.status_code,"json":response.json(),"text":response.text,"content":response.content,"headers":response.headers,})logger.info(f"断言通过:assert{expr}")if__name__=='__main__':#Response就是模拟requestsHTTP请求工具的返回结果对象classResponse(object):status_code=400text="对不起,登陆失败!"content="对不起,登陆失败!"headers=[]@classmethoddefjson(cls):return{"id":1},assert_list=["code==400","'失败'intext",]assertor(assert_list,Response())tests/users/test_login.py,代码:
gitadd.gitcommit-m"feature:基于yaml数据驱动生成测试用例"#推送代码记录到远程服务器的代码仓库gitpushoriginmasterExcel在测试开发中,如果测试用例数量太多,使用yaml也存在很大的维护成本,此时可以考虑使用Excel或者数据库保存更多的测试用例,python中操作Exeel文件的模块有很多,常用的有:xlrd+xlwt,pyexcel+openpyxl等等。
安装模块
pipinstallxlrdpipinstallxlwt封装Excel工具类utils/excel.py,代码:
gitadd.gitcommit-m"feature:封装Excel工具类"#推送代码记录到远程服务器的代码仓库gitpushoriginmaster基于excel文件实现数据驱动生成测试用例tests/users/user_login.py,代码:
gitadd.gitcommit-m"feature:基于excel数据驱动生成测试用例"#推送代码记录到远程服务器的代码仓库gitpushoriginmaster认证测试基于jwt实现登陆正确,并在pytest中基于conftest脚手架使用生成器保持登陆状态
在用户注册/登陆以后,往往项目会返回登陆状态(jwt,session,cookie)提供给客户端,所以上面我们所实现的mockserver实际上是有问题的。因此接下来我们继续来模拟存在jwt认证鉴权的服务端,并在测试框架中基于conftest.py来实现认证测试的这个流程。
安装jwt
pipinstallflask-jwt-extendedconfig.py,代码:
#秘钥,不管是使用session还是jwt认证,都需要对认证的信息鉴权加密SECRET_KEY="ac361a52518d99f4525c1cfe5ba635572190aa6ac52bc8f27ae1b07529feafd0"api/__init__.py,代码:
importconfigfromflaskimportFlaskfromflask_sqlalchemyimportSQLAlchemyfromflask_jwt_extendedimportJWTManager#SQLAlchemy初始化db=SQLAlchemy()app=Flask(__name__)jwt=JWTManager()definit_app():"""服务端初始化"""#加载配置app.config.from_object(config)#加载mysql数据库配置db.init_app(app)#jwt初始化jwt.init_app(app)#自动创建数据表withapp.app_context():db.create_all()returnappapi/views.py,代码:
importpytestimportconfigfromutils.requestorimportRequestfromutils.yamlerimportYamlyaml=Yaml()@pytest.fixture(scope="class",autouse=False)defjwt_token():request=Request()request.logger.info("获取token")data=yaml.read(config.BASE_DIR/"data/test_user.yaml")response=request(data.get("method"),data.get("url"),json=data.get("json"))token=response.json().get("data",{}).get("token")yieldtoken#生成器函数中的暂停关键字,作用是当代码运行到yield时,把yield右边的数据作为返回值提供给调用处,把代码执行权交出去。request.logger.info("移除token")data/test_user.yaml,填写一个保存正确用户信息的用例,方便在conftest中发送正确的账户信息获取token,代码:
importallureimportpytestimportconfigfromutils.requestorimportRequestfromutils.yamlerimportYamlfromutils.assertorimportassertoryaml=Yaml()@allure.epic(config.WEB_NAME)@allure.feature("用户模块")@allure.story("用户中心")classTestUser(object):@pytest.mark.usefixtures("jwt_token")@pytest.mark.parametrize("kwargs",yaml.read(config.BASE_DIR/"data/user_info.yaml"))deftest_user(self,jwt_token,kwargs):allure.dynamic.title(kwargs.get('name'))request=Request()request.logger.info(f"开始请求测试接口:{kwargs.get('name')}")data=kwargs.get('request')data['headers']["Authorization"]=data['headers']["Authorization"].format(token=jwt_token)response=request(data.get("method"),data.get("url"),headers=data.get("headers"))assertor(kwargs.get("assert"),response)data/user_info.yaml,代码:
思路:
问题:如何获取域名?
Jenkins篇:
持续交付是一种软件开发实践。通过持续交付,系统可以自动构建和测试代码更改,并为将其发布到生产环境做好准备。持续交付可以在构建阶段后将所有代码变更都部署到测试环境和/或生产环境中,从而实现对持续集成的扩展。当持续交付得以正确实施时,开发人员将始终能够获得一个已通过标准化测试流程的部署就绪型构建工件。
参考:
前提:有java环境
支持各种的平台:
最低推荐配置:
为小团队推荐的硬件配置:
软件配置:
本次安装环境:阿里云服务器(centos7.4)+docker19.03.8
常用的镜像有两个:
管理jenkins--->mangesplugins--->可选插件,搜索要安装的插件,可选择,安装并且重启jenkins
由于下载地址是插件官网,可能会导致下载失败,然后安装失败.....
如果安装失败,就采用第二种方式。
方式2:
然后手动将下载到本地的hpi插件,上传到jenkins。
管理jenkins--->mangesplugins--->高级选项,下拉选择上传插件。点击本地文件上传
完事之后,重启jenkins,插件生效。
管理jenkins--->mangesplugins--->已安装,搜索要卸载的插件,并且勾选然后点击卸载。
修改/var/jenkins_home/hudson.model.UpdateCenter.xml文件,换国内源:
管理jenkins--->全局安全配置,勾选允许用户注册,完事点击保存。
管理jenkins--->管理用户
点击新建用户
创建成功后的用户列表:
这里只能删除普通的用户。
管理jenkins--->管理用户,点击红色按钮进行删除。
确认删除:
由于jenkins要和别的软件或者平台打交道,那么就要拿着先关凭据去做认证。
jenkins主页--->凭据--->全局凭据
此时进入到了全局的凭据列表,列出了所有的凭据。
如何添加凭据呢?
点击左侧的添加凭据按钮。
创建成功,会在凭据列表展示出来,可以点击右侧按钮编辑该凭据。
在凭据列表中,点击指定凭据后的小三角或者右侧的更新按钮,来修改凭据。
凭据列表,选择指定凭据后的小三角,选择删除选项。
确认删除。
在上述的配置GitHub账号密码的凭据中,有的时候会遇到如下问题:
如何解决:
可以使用ssh形式来解决:
github对SSH密钥做了升级,原来的SHA-1,rsa等一些已经不支持了,由于我使用的是rsa,可能和大部分用户一样,所以今天在push代码时候遇到了这个问题,记录以下。
在本机使用git来生成公钥私钥:
在本机(windows)的用户,你的用户下面有个.ssh目录,生成了公钥私钥两个文件。
添加成功。
在jenkins中,凭据管理下的凭据列表,添加一个凭据。
添加成功后的凭据列表:
如在job中:
不同的镜像依赖的基础镜像不同,导致容器内容的包管理工具也不同,如何查看以来的基础镜像:
[root@rdocker_data]#dockerexec-it-urootmyjenkinsbashbash-4.4#cat/etc/issueWelcometoAlpineLinux3.9Kernel\ronan\m(\l)常见的基础镜像的包管理工具有:
常用的apk的操作:
安装python3.6
bash-4.4#apkupdate-ybash-4.4#apkaddgccbash-4.4#apkaddbuild-basebash-4.4#apkaddzlib-devapksearchpython3apkaddpython3=3.6.9-r2bash-4.4#python3-VPython3.6.9bash-4.4#pip3-Vpip18.1from/usr/lib/python3.6/site-packages/pip(python3.6)pip3install--upgradepip参考:
在系统配置选项,下拉选择邮件通知选项:
如何配置?
后续的构建中,在构建后的操作中,就会自动发邮件。
常用的操作:
配置构建环境中的参数,后续再构建中,能直接是用的参数。
#Exampleofjobdefinition:#.----------------minute(0-59)#|.-------------hour(0-23)#||.----------dayofmonth(1-31)#|||.-------month(1-12)ORjan,feb,mar,apr...#||||.----dayofweek(0-6)(Sunday=0or7)ORsun,mon,tue,wed,thu,fri,sat#|||||#*****user-namecommandtobeexecuted分时日月周示例:
点击添加构建步骤:
常用的有:
allurecommandline会自动从$ALLURE_HOME目录读取json数据,生成allure报告。
注意,json数据目录在项目根目录下的allure-results目录;生成的报告在项目根目录下的allure-report目录中。我们可以通过在项目目录下看到。
就差左下角的保存按钮了。
我已经将制作好的镜像上传到了dockerhub上,咱们直接拉取即可:
dockerpullwangzhangkai/jenkins:1.0然后启动:
dockerrun\-uroot\--namemyjenkins\--restart=always\-d\-p6010:8080\-p50000:50000\--envJAVA_OPTS="-Xmx1024m"\-eJAVA_OPTS=-Duser.timezone=Asia/Shanghai\-v/etc/localtime:/etc/localtime\-v/tmp/jenkins_home:/var/jenkins_home\-v/var/run/docker.sock:/var/run/docker.sock\wangzhangkai/jenkins:1.0参考上述命令启动即可。
一般在提交的时候,遇到该问题。
解决,管理jenkins---->全局安全配置,下拉选择扩展请求保护,勾选启用代理兼容,下拉保存。
dockerrun\-uroot\--namemyjenkins\--restart=always\-d\-p6010:8080\-p50000:50000\--envJAVA_OPTS="-Xmx512m"\-eJAVA_OPTS=-Duser.timezone=Asia/Shanghai\-v/etc/localtime:/etc/localtime\-v/docker_data/jenkins_home:/var/jenkins_home\-v/var/run/docker.sock:/var/run/docker.sock\wangzhangkai/jenkins:1.0java.io.IOException:Failedtoload:LockableResourcesplugin(lockable-resources1069.v726298f53f8c)...解决办法:更新最新版本即可
管理所有的接口:
实现:
项目列表:
用例列表:
用例执行日志:
数据可视化:
遇到数据库问题,pycharm中无法连接或者无法识别mysql数据库。可能的原因:
adminLTE:
我们copy的是static/AdminLTE-master/starter.index修改静态文件的引用方式
it表
法1
pipinstallHTMLTestRunner-Python3pipinstallHTMLTestRunner-Python3==0.8.0但经过测试,发现源码有点问题,如果你在使用中遇到报错:
TypeError:abytes-likeobjectisrequired,not'str'就去源码的691行,修改:
#修改前self.stream.write(output)#修改后self.stream.write(output.encode('utf8'))其实源码中也提到了这点!
获取所有的选中状态的复选框:
$("#chk1").find('input:checkbox').each(function(){//遍历所有复选框if($(this).prop('checked')==true){console.log($(this).val());//打印当前选中的复选框的值}});functiongetCheckBoxVal(){//jquery获取所有选中的复选框的值varchk_value=[];$("#chk1").find('input[name="test"]:checked').each(function(){//遍历,将所有选中的值放到数组中chk_value.push($(this).val());});alert(chk_value.length==0'你还没有选择任何内容!':chk_value);}或者:
$("#sure").click(function(){vararr=newArray();$.each($(".p1"),function(index,item){//console.log(index,item)if($(item).get(0).checked){arr.push($(item).val())}});if(arr.length==0){//说明用户未选中用例,需要给提示//console.log(2222222,"未选中",arr);$("#errorMsg").html("请勾选至少一个用例!");}else{//编写后续的操作}});ajax如何处理跨域问题?有以下几种办法:
//序列化字典->字符串JSON.stringify(['A','B'])//反序列化字符串->字典JSON.parse()后端序列化与反序列化#序列化字典->字符串json.dumps(['A','B'])#反序列化字符串->字典json.loads()io在内存中创建一个文件句柄
fromioimportBytesIOfromioimportStringIOf=open('a.html','wb')#BytesIOf1=open('a.html','w',encoding='utf-8')#StringIO可视化前端从后端获取必要的数据,进行渲染展示
pyecharts的使用
使用:
前端
utils/EchartsHand.py
@csrf_exemptdefecharts_show(request):"""可视化"""ifrequest.is_ajax():data_dict={}data_dict.update(EchartsHandler().pie())data_dict.update(EchartsHandler().line_race())returnJsonResponse({'status':True,'data_dict':data_dict})returnrender(request,'echarts_show.html')定时任务常见用于定时任务的:
APScheduler
#!/usr/bin/envpythonimportosimportsysimportthreadingfromutils.CrontabHandimportrunif__name__=="__main__":os.environ.setdefault("DJANGO_SETTINGS_MODULE","auto_port.settings")try:fromdjango.core.managementimportexecute_from_command_lineexceptImportError:#Theaboveimportmayfailforsomeotherreason.Ensurethatthe#issueisreallythatDjangoismissingtoavoidmaskingother#exceptionsonPython2.try:importdjangoexceptImportError:raiseImportError("Couldn'timportDjango.Areyousureit'sinstalledand""availableonyourPYTHONPATHenvironmentvariableDidyou""forgettoactivateavirtualenvironment")raise#起一个线程执行定时任务#threading.Thread(target=run).start()execute_from_command_line(sys.argv)utils/CrontabHand.py
settings.py配置:
有两种常用的文件上传方式:
ajax上传
upload.html
把预览完成
使用echarts展示三个图:
每天凌晨1点30分50秒,检查it表,it_end_time是当前日期的接口项目,提取出来,获取该项目下所有的用例,批量执行一次,并生成log日志。
扩展:使用django发邮件,附件是批量执行的报告
建议:
多线程执行该任务
定时任务:-需求是:每天的凌晨1:30:20去检查it表,查看it_end_time是当天的,就把其关联的所有的用例执行一遍。-问题:如何实现?思路是随着django环境的运行,启动一个线程,调用一个job来实现需求。
UI测试(UserInterfaceTest,译作:用户界面测试),UI测试侧重产品的UI交互是否正确,模拟后端进行测试也可以,放在单元测试里去做也可以。
常用的web自动化测试工具有:selenium,Playwright、QTP、Cypress、Rapise、TestProject、Ranorex、Katalon、Subject7、Serenity等等
以下内容可参考:
提供了脚本录制功能和导出脚本。
火狐浏览器安装
火狐的插件管理中搜索seleniumide.
将该插件添加到浏览器。
添加成功如下:
在Google应用商店
添加该扩展程序。
解决:无法访问谷歌应用商店的问题:
Selenium是由杰森·哈金斯(JasonHuggins)于2004年在思特沃克(ThoughtWorks)开发的一款专门为Web应用而开发的自动化测试工具(最早是作为火狐浏览器的一个插件存在,内核基于javascript实现),适合进行功能测试、验收测试,同时支持所有基于web的管理任务自动化。简单来说,就是用程序模拟人操作浏览器网页,可以实现UI自动化测试。
Selenium基于webdriver协议可以控制浏览器模仿用户操作完成UI自动化测试,同时它也是一个生态系统,其主要功能包括:测试与浏览器的兼容性,测试应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能,创建回归测试检验软件功能和用户需求。Selenium包含了SeleniumIDE、SeleniumRC(Selenium2.0以后已经webdriver替代了)、SeleniumGrid。
Selenium发展到今天已经经历4个版本:Selenium1.0,Selenium2.0,Selenium3.0,Selenium4.0
特点:
注意:UI自动测试依赖于浏览器环境,所以浏览器务必不要设置为自动更新!!!下载的浏览器webdriver驱动一定要对应当前测试的浏览器版本。
selenium配置Chrome的驱动,该驱动需要和Chrome的版本保持一致(大版本)
安装webdriver驱动
python中安装selenium
解决方案:
fromselenium.webdriver.common.action_chainsimportActionChains基本使用
测试页面: