python接口自动化-实战(第二阶段)

再次强调,因为视频中实战地址已无法访问,建议大家根据原理,用自己公司的业务逻辑、代码来练手。本人公司使用pyhon2.7,所以语法可能与3.x不同

目标

  • 复习单元测试:引入单元测试、html测试报告、断言结果
  • 引入超继承(二选一)
  • 引入ddt(二选一)
  • 添加一个字段存入测试结果
  • 引入try...except...finally
  • 完成用例的可配置化,想跑哪条用例,通过配置文件配置好
  • 难点:
    • 同时跑多个模块怎么跑

复习单元测试

使用unittest,测试我们自己封装的http请求类-HttpRequest,目的一:学习如何进行单元测试,目的二:写出咱们的各个接口。

  • 新建test_http_request.py编写测试用例

    import json
    import unittest
    from common.public.http_request import HttpRequest
    
    class TestHttpRequest(unittest.TestCase):
    
        def setUp(self):
            pass
    
        def tearDown(self):
            pass
        
        #登录接口
        def test_login(self): 
            payload = {"user_phone": "1801923****", "device_model": "iphone7"}
            res=HttpRequest().http_request("1.6.0", "android", "785c6fee0e4488ca412a5afc9a00e9d8","/login","post",payload)
            self.assertEqual(200,res.status_code)  #添加断言
            print ("获得的结果是:",res.json()) 
    
  • run.py文件中加载用例、执行用例

    import unittest
    from common.public.test_http_request import TestHttpRequest
    
    suite=unittest.TestSuite()
    suite.addTest(TestHttpRequest("test_login")) #添加用例
    
    #执行用例
    runner=unittest.TextTestRunner()
    runner.run(suite)
    
  • 使用HTMLTestRunner生成HTML测试报告

    下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html

    使用:

      - HTMLTestRunner是Python标准库的unittest模块的扩展,无法通过pip安装
      - 下载HTMLTestRunner.py放在lib目录下
      - 导入模块 `import HTMLTestRunner`
    

    注意点:

    代码改造为使用HTMLTestRunner生成HTML测试报告:

    # encoding:utf-8
    
    import sys
    reload(sys)
    sys.setdefaultencoding('utf8') #python2.7需要添加,否则会因编码问题报错
    
    import HTMLTestRunner
    import unittest
    from common.public.test_http_request import TestHttpRequest
    
    suite=unittest.TestSuite()
    suite.addTest(TestHttpRequest("test_login")) #添加用例
    
    with open("test_result/rusult.html","wb") as file:
        #执行用例
        runner=HTMLTestRunner.HTMLTestRunner(stream=file,verbosity=2,title="测试HttpRequest类")
        runner.run(suite)
    

    获得的html格式的测试报告:

    image

引入ddt

引入ddt的目的是为了数据分离、数据驱动测试。所谓数据驱动,从它的定义来看,就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变。说的直白些,就是参数化的应用。值得注意的是,ddt适用于测试数据是列表嵌套列表或者列表嵌套字典。

  • 给测试数据中新增一列expected,用于添加断言的预期结果


    image
  • 修改do_excel.py,读取expected的数据:

    get_data()方法中新增:sub_data["expected"] = sheet.cell(i, 6).value
    write_back_data()方法中修改:sheet.cell(i,7).value=value

  • 改造测试用例test_http_request.py

    import unittest
    from common.public.http_request import HttpRequest
    from ddt import ddt,data
    from common.public.do_excel import DoExcel
    
    test_data=DoExcel().get_data("../../test_data/test_data.xlsx","Sheet1")
    
    @ddt
    class TestHttpRequest(unittest.TestCase):
    
        def setUp(self):
            pass
    
        def tearDown(self):
            pass
        @data(*test_data)
        def test_login(self,item): #添加item,即测试数据
            res=HttpRequest().http_request(item["url"],item["method"],eval(item["payload"]))
            self.assertEqual(str(item["expected"]),str(res.status_code))
            print "获得的结果是:",res.json()
    
  • 改造run.py

    import HTMLTestRunner
    import unittest
    from common.public.test_http_request import TestHttpRequest
    
    suite=unittest.TestSuite()
    loader=unittest.TestLoader()
    suite.addTest(loader.loadTestsFromTestCase(TestHttpRequest))
    
    with open("test_result/rusult.html","wb") as file:
        #执行用例
        runner=HTMLTestRunner.HTMLTestRunner(stream=file,verbosity=2,title="测试HttpRequest类")
        runner.run(suite)
    
    • 问题:因为test_http_request.pyrun.py不是一个层级,所以这样运行run.py会报错,找不到test_data.xlsx,所以在test_http_request.py中的test_data地址改为绝对路径。but,如果设置为绝对路径,那么在自己电脑上能跑,在别人电脑上可能会失败。

    • 解决方案:路径可配置

      • os.path.split()函数,将文件名和路径分割开
      • os.path.split(path)[0],取上级目录
      • os.path.join(path,*paths),拼接路径不用加/
      import os
      """专门读取路径的值"""
      
      #获取当前文件所在绝对路径,包括模块名,__file__表示是当前文件本身
      path=os.path.realpath(__file__)
      
      #获取顶级目录的绝对路径
      project_path=os.path.split(os.path.split(os.path.split(path)[0])[0])[0]
      
      #获取测试数据的绝对路径
      test_data_path=os.path.join(project_path,"test_data","test_data.xlsx")
      
      #获取测试报告的绝对路径
      test_result_path=os.path.join(project_path,"test_result","html_report","result.html")
      
      • 使用project_path.py中的路径
        • 引入project_path.pyfrom common.public.project_path import *
        • test_http_request.py中:test_data=DoExcel().get_data(test_data_path,"Sheet1")
        • run.py中:with open(test_result_path,"wb") as file:

添加字段存入测试结果

1.result字段存放response.json()

添加:DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json))

# encoding: utf-8
import json
import unittest
from common.public.http_request import HttpRequest
from ddt import ddt,data
from common.public.do_excel import DoExcel
from common.public.project_path import *

test_data=DoExcel().get_data(test_data_path,"Sheet1")

@ddt
class TestHttpRequest(unittest.TestCase):

    def setUp(self):
        pass

    def tearDown(self):
        pass
    @data(*test_data)
    def test_login(self,item):
        res=HttpRequest().http_request(item["url"],item["method"],eval(item["payload"]))
        self.assertEqual(str(item["expected"]),str(res.status_code))  
        #将res.json写入excel中
        DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json)) 
        print "获得的结果是:", res.json()

  • 问题:一,报错时,因为执行不到print语句,从result.html中无法看出时什么问题。二,如果报错时,res.json()就无法写入到excel文件中。
  • 解决:使用finally,是无论有没有捕获到异常都执行finally下的代码(代码在后面展示)

2.新增一个TestResult字段存放测试结果

与result字段不同,result字段存放的是response.json(),TestResult字段存入Pass/Failed,表示用例成功还是失败。写入excel后可以直接通过excel筛选,只查看为Failed的用例

  • 首先在test_data.xlsx中添加字段TestResult

    image
  • 改造测试用例test_http_request.py

    test_data=DoExcel().get_data(test_data_path,"Sheet1")
    
    @ddt
    class TestHttpRequest(unittest.TestCase):
    
        def setUp(self):
            pass
    
        def tearDown(self):
            pass
            
        @data(*test_data)
        def test_login(self,item):
            res=HttpRequest().http_request(item["url"],item["method"],eval(item["payload"]))
            #捕获异常
            try:
                self.assertEqual(str(item["expected"]),str(res.status_code)) 
                #如果没有异常, TestResult="Pass"
                TestResult="Pass"
            except AssertionError as e:
                #如果有异常, TestResult="Failed"
                TestResult = "Failed"
                print "测试用例失败,{0}".format(e)
                raise e
            #无论是否捕获异常都执行
            finally:
                print "获得的结果是:", res.json()
                DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json())
    
  • 改造do_excel.py写入TestResult字段

    def write_back_data(self,file_name,sheet_name,i,result,TestResult):
        wb=load_workbook(file_name)
        sheet=wb[sheet_name]
        sheet.cell(i,7).value=result
        sheet.cell(i,8).value=TestResult
        wb.save(file_name) #保存
    

    同时,test_http_request.py中添加TestResult字段:
    DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json(),TestResult)

用例可配置

实现多个用例同时执行、多个模块同时执行

配置文件效果:

[MODE]
mode={"login":"all",
      "register":[1,2,4,5],
      "recharge":[1,2]
      }

通过option和value,配置不同的用例,option是对应的模块(使用不同的sheet放置不同的模块,对应sheetname),value的all代表该模块所有用例,列表里的数字代表case_id,

  • 新建一个配置文件case.conf
  • 写一个读取配置文件的类
import configparser

class ReadConfig:

    @staticmethod
    def get_config(file_path,setion,option):
        cf=configparser.ConfigParser()
        cf.read(file_path)
        return cf[setion][option]
        
if __name__ == '__main__': #测试
    from common.public import project_path
    print ReadConfig.get_config(project_path.case_config_path,"MODE","mode")

控制台输出:

image
  • 改造do_excel.py

    class DoExcel:
    
        @staticmethod  #静态方法,直接调用
        def get_data(file_name):
            wb=load_workbook(file_name)
            mode=eval(ReadConfig.get_config(project_path.case_config_path,"MODE","mode"))
    
            test_data=[]
            #循环配置文件中的mode的key
            for key in mode:
                sheet=wb[key] 
                #如果为all,跑所有的case
                if mode[key]=='all':
                    for i in range(2,sheet.max_row+1):
                        row_data={}
                        row_data["case_id"] = sheet.cell(i, 1).value
                        row_data["url"]=sheet.cell(i,2).value
                        row_data["method"]=sheet.cell(i,3).value
                        row_data["payload"]=sheet.cell(i,4).value
                        row_data["title"]=sheet.cell(i,5).value
                        row_data["expected"] = sheet.cell(i, 6).value
                        #读取sheet_name
                        row_data["sheet_name"]=key
                        test_data.append(row_data)
                else:
                #如果不是all,按case_id跑
                    for case_id in mode[key]:
                        row_data = {}
                        row_data["case_id"] = sheet.cell(case_id+1, 1).value
                        row_data["url"] = sheet.cell(case_id+1, 2).value
                        row_data["method"] = sheet.cell(case_id+1, 3).value
                        row_data["payload"] = sheet.cell(case_id+1, 4).value
                        row_data["title"] = sheet.cell(case_id+1, 5).value
                        row_data["expected"] = sheet.cell(case_id+1, 6).value
                        row_data["sheet_name"] = key 
                        test_data.append(row_data)
    
            return test_data
    
        @staticmethod
        def write_back_data(file_name,sheet_name,i,result,TestResult):
            wb=load_workbook(file_name)
            sheet=wb[sheet_name]
            sheet.cell(i,7).value=result
            sheet.cell(i,8).value=TestResult
            wb.save(file_name) #保存
    
    
  • 改造测试用例test_http_request.py

    修改sheet_name参数化:

    DoExcel().write_back_data(test_data_path,item["sheet_name"],item["case_id"]+1,str(res.json()),TestResult)
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容