接口自动化总结

写下本文的目的

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

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

基础思路


每条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存储这些数据,方便维护。

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




  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值