欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

接口自动化总结

程序员文章站 2022-05-23 15:17:17
...

写下本文的目的

 对半年多来的接口自动化测试工作做一个总结,同时也希望能够有志同道合的朋友一起交流,本文中仅贴出了接口框架里面部

分代码,更多的是希望有朋友一起讨论下接口框架的实现思路、应有的功能、可维护性等。

基础思路

接口自动化总结

每条Case继承与TestCase基类,TestCase基类中,实现了每条Case工作流程:

InitTest(初始化,一般没用到) à PreTest(可以理解为前置条件,例如数据库操作产生数据)

à RunTest(测试执行时的具体逻辑,只包含逻辑,不包含代码,代码在TestModule中封装好了)à PostTest(清理数据,确保单条Case可以重复执行)

 

TestModule:封装每个接口的模板类,每个接口对应于一个方法,参数由定参、默认参数、变参组成,每条Case调用TestModule中封装好的方法,根据变参组合,设计出不同的Case。

TestModule继承于Common公共类,Common中封装了requests的http请求


TestModule中一个退出登录接口方法的封装例子:

   

deflogout(self,UserID,SessionID):      #UserID SessionID为变参数

        '''退出登录接口

        '''
        path = '/Users/Logout.ashx'   #接口路径

        requestData = {

                        "UserID":UserID,

                        "SessionID":SessionID
                        }

        requestData = dict(requestData,**self.commonparam_adr)

# self.commonparam_adr为公共参数,即定参

        resJson = self.httpPost(requestData,self.module,path)

#         print resJson

       returnresJson

具体的一个Case:

class logout(TestCase):
    '''【退出登录】退出登录
    '''
    owner = 'yuziqiang'
    timeout = 5
        
    def runTest(self):
        global m
        m =  TestModule()
        #登录
        res1 = m.login(User = 10086, pwd = ***)  
        #注销传入的UserID、SessionID为登录返回的UserID、SessionID
        UserID = res1['UserID']
        SessionID = res1['SessionID']


        #注销      
        resJson = m.logout(UserID, SessionID = SessionID)
        self.assertEqual('error_code', resJson['error_code'], '0')    #对接口返回的标志性参数断言,例如此处的接口反馈码

可以看出每个Case里面基本上只有业务逻辑

 

测试用例批量运行

 接口自动化总结

TestRunner

负责载入与筛选测试用例集,对筛选后的用例起线程运行。

熟悉UnitTest+HtmlRunner框架的,可以讲本文中的TestRunner理解为UnitTest中的TestLoader


筛选准则:

单个测试用例类是否继承于TestCase基类,是否有owner子段,是否有Runtest方法

defloadTestsFromClass(self, testclass):   

        """返回测试用例list"""

        testclasses = []

        if  isinstance(testclass, types.TypeType)and \

            issubclass(testclass, TestCase)and \

            hasattr(testclass,"runTest"):     #判断这个类是否满足正常的要求,满足就将该条用例的类名称添加到列表中

            testclasses.append(testclass)

        return testclasses


TestReport

每条Case执行后,都会产生xml日志,TestReportTestRunner会处理这些xml日志,生成Html文档,将Html报告通过邮箱的形式发送到相关负责人

# -*- coding: utf-8 -*-

"""

创建测试报告并以QQ邮箱的形式发送

"""

importsocket

importsys, os, smtplib

importurllib

importwin32com.client

fromemail.MIMETextimportMIMEText

fromemail.headerimportHeader


classEnumEmailPriority(object):

   """邮件优先级

    """

   Low = 'Low'

   Normal = 'Normal'

   High = 'Hight'
 

defsendemail(htmldoc, recipents, title,priority=EnumEmailPriority.Normal):

   """发送邮件

   

    @typehtmldoc: string

    @param htmldoc:邮件内容,必须为utf8编码的字符串

    @typerecipents: string

    @param recipents:邮件接收人,若存在多个接收人,请使用分号";"隔开。

    @type title: string

    @param title:邮件标题,必须为utf8编码的字符串

    """

 

   usr_from='*******@qq.com'

   pwd=******

   htmldoc = MIMEText(htmldoc,"html")

   htmldoc.set_charset("gb2312")

   htmldoc["Subject"] = Header(title,'utf-8').encode()

 

   ifisinstance(recipents, list):

        htmldoc["To"] =";".join(recipents)    #htmldoc['To']接收的是字符串而不是list,如果有多个邮件地址,用;分隔即可
       

   else:

        htmldoc["To"] = recipents     

   htmldoc["From"] = usr_from

   smtp_server = 'smtp.qq.com'

   server = smtplib.SMTP_SSL(smtp_server,465)

   server.login(usr_from, pwd)

   importstring

   recipents = string.splitfields(recipents,",")

#     print recipents ,type(recipents)

   server.sendmail(usr_from, recipents, htmldoc.as_string())

   server.quit()

defprint_usage():

   USAGE=\

u"""

用法:%(Program)s测试结果目录 邮件标题  邮件帐号

 

测试结果目录:存放用例运行结果的目录,就是包含TestResults.xml的目录

邮件帐号:测试报告需要发送的邮件帐号,以逗号隔开。

"""

   print USAGE % {"Program":os.path.basename(sys.argv[0])}   #获取当前py文件名,即testreport.py

   

if__name__ =='__main__':

    
   if len(sys.argv) !=4:

        print_usage()

        sys.exit(0)    #正常退出

   rstdir = sys.argv[1]   

#     mail_title = sys.argv[2].decode('gbk').encode('utf8')

   mail_title = sys.argv[2]

   recipents = sys.argv[3].split(',')

   rstdir = rstdir.decode('gbk')

   xmlrst = os.path.join(rstdir, "RunResults.xml")

   xsltfile = os.path.join(rstdir, "RunResult.xsl")


   #将TestResult和Module的log属性设为绝对路径

   importxml.dom.minidomasdom

   xmlstream = open(xmlrst)

   xmldata = xmlstream.read()

   xmlstream.close()

   xmldom = dom.parseString(xmldata)

   rstnodes = xmldom.getElementsByTagName('TestResult')

 

   for nodein rstnodes:

        log = node.attributes['log'].nodeValue

        dirname = rstdir.split('\\')

        pcname = socket.gethostname()

        #获取本机电脑名

        hostname =socket.getfqdn(socket.gethostname())

        #获取本机ip

        hostip = socket.gethostbyname(hostname)

        log = 'ftp://' + str(hostip) +'/result/' +  dirname[len(dirname)-1] +'/' + log

        

        #node.attributes['log'].nodeValue = os.path.join(rstdir, log)

        node.attributes['log'].nodeValue = log   

       

       

   rstnodes = xmldom.getElementsByTagName('Module')           

   for nodein rstnodes:

        log = node.attributes['log'].nodeValue

        node.attributes['log'].nodeValue =os.path.join(rstdir, log)

      

   #将xml转换为html

   xmlsource = win32com.client.Dispatch('MSXML2.DOMDocument')

   xmlsource.loadXML(xmldom.toxml())

   stylesheet = win32com.client.Dispatch('MSXML2.DOMDocument')

   stylesheet.load(xsltfile)

   htmldoc = xmlsource.transformNode(stylesheet)

   htmldoc = htmldoc.encode('gbk')

   htmldoc = urllib.unquote(htmldoc)

sendemail(htmldoc,','.join(recipents),mail_title)

 

生成的Html报告

接口自动化总结

 

用例Log:

ftp链接访问

接口自动化总结

    

TestExcute

传入TestRunner以及TestReport中的初始数据,例如指定testrunner中运行哪个模块包下的用例、执行何种优先级的用例;指定TestReport中目标收件人、邮件标题等

 

附:xml日志转换为xls表格:

xmlIntoXls

接口自动化总结

xmlIntoXlsxml日志中提取出需要用到的数据,例如,用例描述、负责人。优先级、测试结果等,生成xls测试结果报告。

 

xml日志如下:

接口自动化总结

  

xmlIntoxls代码如下:

#-*- coding: utf-8 -*-

"""将XML文档的测试结果对应转换为result.xls"""

 

importos

importxml.etree.ElementTreeas ET

fromxlwtimport*

importxlwt

from_ctypesimportalignment

 

 

classxml_into_xls():

   def__init__(self):

        self.keyword = ['测试用例','用例描述','负责人','状态','耗时','测试结果'] #每列字样

        self.style = XFStyle()

        self.datatable=xlwt.Workbook(encoding='utf-8')

        self.newsheet = self.datatable.add_sheet('mxxx',cell_overwrite_ok=True)

        #新建excel文档sheet 第二个参数允许覆盖

       

        self.newsheet.col(0).width =18000       #设置单元格第0列宽度

        self.newsheet.col(1).width =15000

      

        self.newsheet.col(2).width =3000

        self.newsheet.col(3).width =3000

        self.newsheet.col(4).width =3000

        self.newsheet.col(5).width =3000

       

   defgetTrEle(self):

        """获取xml中"TestResult"子目录对应的元素列表

        """

        xmlrst =os.path.join(os.path.dirname(__file__),"RunResults.xml")

        tree=ET.parse(xmlrst)

        root=tree.getroot()     #获取根目录

        # print root

       

        testsuite_per=root.findall("TestResult")   #获取根目录下的子目录

        # print testsuite_per

       

        return testsuite_per

   

   defsetstyle(self,style_num):

        """"style_num参数为整形

            style_num=0 :标题风格

            style_num=1 :结果正确时奇数行风格

            style_num=2 :结果正确时偶数行风格

            style_num=3 :结果错误时奇数行风格

            style_num=4 :结果错误时偶数行风格

        """

        if style_num ==0:

            pattern = Pattern()                #创建一个模式                                       

            pattern.pattern =Pattern.SOLID_PATTERN    #设置其模式为实型             

            pattern.pattern_fore_colour =7   #常用颜色可设置为22,26,7,42                           

            #设置单元格背景颜色 0 = Black, 1 = White,2 = Red, 3 = Green, 4 = Blue, 5 = Yellow, 6 =Magenta,

            self.style.pattern =pattern            #将赋值好的模式参数导入Style                                 

            #创建一个Line_data列表,并将其值赋为测试表,以utf-8编码时中文前加u

           

           

           

            fnt = Font()                       #创建一个文本格式,包括字体、字号和颜色样式特性                             

            fnt.name = u'微软雅黑'               #设置其字体为微软雅黑                                 

            fnt.colour_index = 0              #设置其字体颜色       0:黑  1:白  2:红  5:黄                            

            fnt.height = 0x00e8

            fnt.bold = True                                            

            self.style.font = fnt 

           

            alignment = Alignment()

            alignment.horz = 0x02      #0:常规 1:水平左对齐  2:水平居中

            self.style.alignment =alignment

           

           

            borders = Borders()    #设置下框线样式                                     

            borders.left = 1      # 1为常规框线                                   

            borders.right = 1                                          

            borders.top = 1                                            

            borders.bottom = 1                                         

            self.style.borders = borders

            returnself.style

       

        elif style_num ==1:

            pattern = Pattern()                #创建一个模式                                       

            pattern.pattern =Pattern.SOLID_PATTERN    #设置其模式为实型             

            pattern.pattern_fore_colour =42   #常用颜色可设置为22,26,7,42                          

            #设置单元格背景颜色 0 = Black, 1 = White,2 = Red, 3 = Green, 4 = Blue, 5 = Yellow, 6 =Magenta,  the list goes on...

            self.style.pattern =pattern 

           

            self.style.font=xlwt.Font()    #恢复默认值

            self.style.alignment=xlwt.Alignment()

            returnself.style

       

        elif style_num ==2:

            self.style.pattern=xlwt.Pattern()

            self.style.font=xlwt.Font()    #恢复默认值

            self.style.alignment=xlwt.Alignment()

            returnself.style

       

        elif style_num ==3:

            pattern = Pattern()                #创建一个模式                                       

            pattern.pattern =Pattern.SOLID_PATTERN    #设置其模式为实型             

            pattern.pattern_fore_colour =42   #常用颜色可设置为22,26,7,42                          

            #设置单元格背景颜色 0 = Black, 1 = White,2 = Red, 3 = Green, 4 = Blue, 5 = Yellow, 6 =Magenta,  the list goes on...

            self.style.pattern = pattern

 

            fnt = Font()                       #创建一个文本格式,包括字体、字号和颜色样式特性                             

#             fnt.name = u'微软雅黑'                #设置其字体为微软雅黑                                

            fnt.colour_index = 2              #设置其字体颜色       0:黑  1:白  2:红  5:黄                            

            fnt.height = 0x00c8

            fnt.bold = True                                            

            self.style.font = fnt 

#             self.style.font=xlwt.Font()     #恢复默认值

 

            self.style.alignment=xlwt.Alignment()     #恢复对其格式的默认值

 

            returnself.style

       

        elif style_num ==4:

            self.style.pattern=xlwt.Pattern()

            self.style.font=xlwt.Font()    #恢复默认值

            self.style.alignment=xlwt.Alignment()

           

           

            fnt = Font()                       #创建一个文本格式,包括字体、字号和颜色样式特性                             

#             fnt.name = u'微软雅黑'                #设置其字体为微软雅黑                                 

            fnt.colour_index = 2              #设置其字体颜色       0:黑  1:白  2:红  5:黄                            

            fnt.height = 0x00c8

            fnt.bold = True                                            

            self.style.font = fnt 

#             self.style.font=xlwt.Font()     #恢复默认值

 

            returnself.style

   defwriteBody(self,row,num_style):

        '''写入第row行的测试用例结果

        '''

        testsuite_per = self.getTrEle()

        self.newsheet.write(row+1,0,testsuite_per[row].get('name'),self.setstyle(num_style))

        self.newsheet.write(row+1,1,testsuite_per[row].get('casemark'),self.setstyle(num_style))

        self.newsheet.write(row+1,2,testsuite_per[row].get('owner'),self.setstyle(num_style))

        self.newsheet.write(row+1,3,testsuite_per[row].get('status'),self.setstyle(num_style))

        self.newsheet.write(row+1,4,testsuite_per[row].get('duration'),self.setstyle(num_style))

        self.newsheet.write(row+1,5, testsuite_per[row].get('result'),self.setstyle(num_style))

          

       

   defwritExcel(self):

        '''写入标题、测试用例Body结果

        '''

 

        testsuite_per = self.getTrEle()    #从Runresult.xml中获取每条用例xml列表

        for iin range(len(self.keyword)): #写入标题                                                                              

            self.newsheet.write(0, i, self.keyword[i],self.setstyle(0))

           

        for row_resultinrange(0,len(testsuite_per)):

           

            if row_result %2==0:     #偶数行风格

               

                if testsuite_per[row_result].get('result') =="True":

                    self.writeBody(row_result,1)

                else:

                    self.writeBody(row_result,3)

 

            elif row_result %2==1:   #奇数行风格

               

                if testsuite_per[row_result].get('result') =="True":

                    self.writeBody(row_result,2)

                else:

                    self.writeBody(row_result,4)

                   

   defrun(self):

        self.writExcel()

        self.datatable.save('result.xls')  #保存结果名字为result.xls

       

if__name__ =='__main__':

   turn = xml_into_xls()

   turn.run()


总结

 框架中的测试数据都直接写在代码里面了,后续考虑提取出用例里面使用的基础数据,例如账号、api地址、公共参数、邮箱收件人等,计划使用yaml存储这些数据,方便维护。

 后期会不定期更新博文,包括但不限于接口脚本。