接口测试框架(三)-框架优化

参考博客:https://www.bstester.com/2015/08/interface-test-automation-scheme-details

看到一篇接口自动化测试的文章,觉得这个文章里的方案更加的容易编写测试用例。优点是测试用例的参数配置很方便,直接用excel表格中写入自己希望执行的用例就行了,有点像JMter。


用这种方法的话,测试用例统一使用excel文档保存,结合之前的接口测试框架能够更加清晰,简单的写出测试用例。因为看上去太好用了(不用为每一个接口测试用例单独写一个方法,简直太省事了),我就将这个方法加入到测试框架中了。


* 一、加入读取、写入Excel文件功能

第一步,在文件读取类中加入读取与写入excel文件的类。

from xlrd import open_workbook
from xlutils.copy import copy
from xlwt import Style

class ExcelReader():
    def __init__(self, excel_file, sheet, title_line=True):
        if os.path.exists(excel_file):
            self.excel_file = excel_file
        else:
            raise FileNotFoundError('File not found!%s')
        self.sheet = sheet
        self.title_line = title_line
        self.table = None
        self._data = list()

    def set_value(self,col, row, value):
        #   将传入值写进xlsw文件相应的行号,列号中
        rb = open_workbook(self.excel_file, formatting_info=True)
        wb = copy(rb)
        ws = wb.get_sheet(0)
        ws.write(col, row, value, Style.default_style)
        wb.save(self.excel_file)

    @property
    def data(self):
        #   读取xlsx文件
        if not self._data:
            work_book = open_workbook(self.excel_file)
            if type(self.sheet) not in [int, str]:
                print '[Excel sheet error]Input <int> <str> please'
            elif type(self.sheet) is str:
                self.table = work_book.sheet_by_name(self.sheet)
            else:
                self.table = work_book.sheet_by_index(self.sheet)
            #   判断是否有标题,标题有无影响数据的格式(可运行最下方main函数进行比较)
            #   有标题:[{标题1:参数1,标题2:参数2,标题3:参数3}]
            #   无标题:[[参数1(行1),参数2(行1),参数3(行1)],[参数1(行2),参数2(行2),参数3(行2)]]
            if self.title_line:
                title = self.table.row_values(0)
                for row in range(1, self.table.nrows):
                    self._data.append(dict(zip(title, self.table.row_values(row))))
            else:
                for col in range(self.table.nrows):
                    self._data.append(self.table.row_values(col))
        return self._data

这里参照了灰蓝的写法,自己再加入了写入excel文件的方法。


* 二、创建测试类用于读取excel中的测试用例

这一步主要是讲落叶归根博客中的方法引入到我们的测试框架中。大致的思路是,将excel中的数据存入我们的用例类中,生成一个个的测试用例。好像就这一块算是我自己独立完成的,哎,要走的路好长呀。

这里有几个很重要的点,分别是:参数、关联、检查点、是否运行

参数:注意一定要是字典类型否则会出错。参数还要支持参数化,例如$token则会去配置文件中读取token的值,并赋值给该参数。

关联:这里还是拿token举例。比如说我先执行登录接口,登录成功后会返回给我们一个token,接下来的测试都需要用到token。那么我们在这里将登录接口中的token设为关联,运行登录测试用例后,就会将刚获取的token的值存入配置文件中。

检查点:这个比较简单,看响应内容中是否有包含该字符串。

是否运行:这里的值设为yes就会执行,没有的话就不会执行。


#!/usr/bin/env python
#coding=utf-8

from utils.file_reader import YamlReader
from utils.config import BASE_PATH
from utils.client import HTTPsClient
from utils.log import logger
import yaml
import json
import os


class InterfaceCase(object):
    #   接口测试对象,属性对应接口测试xlsx文件。方法有建立用例数据,运行用例数据
    def __init__(self, data):
        self.data = data
        self.no = int('%d' % data['No.'])
        self.apiName = data['API']
        self.testCase = data['TestCase']
        self.host = data['HOST']
        self.url = data['URL']
        self.requestMethod = data['RequestMethod']
        self.params = data['Params']
        self.relation = data['Relation']
        self.checkPoint = data['CheckPoint']
        self.active = data['Active']
        self.result = 'Na'
        self.response = None
        self.y = YamlReader(os.path.join(BASE_PATH, 'config', 'test.yaml'))
        self.get_config()

    def __str__(self):
        #   漂亮的输出测试用例的重要属性
        return 'No.' + str(self.no) + ' API:' + self.apiName + ' TestCase:' + self.testCase + \
          ' result:' + self.result

    def get_config(self):
        #   将参数中带有$的参数与配置文件中的参数匹配,并赋值
        #   例如:参数中有$token,则将配置文件中token的值付给token这个参数
        try:
            params = json.loads(self.params)
        except ValueError:
            logger.warn('No.%s Params input error!' % self.no)
            return
        data = self.y.data
        for i in params:
            if '$' in params[i]:
                try:
                    params[i] = data[0][i]
                except KeyError:
                    raise KeyError('No.%s Params "%s" not found!' % (self.no, i))
        self.params = params

    def run(self):
        #   运行用例,暂时只支持POST以及GET方法。client.py中可支持更多参数配置
        h = HTTPsClient(url=self.host+self.url, method=self.requestMethod)
        if self.requestMethod == 'POST':
            self.response = h.json_transform_dict(h.send(data=self.params))
        elif self.requestMethod == 'GET':
            self.response = h.json_transform_dict(h.send(params=self.params))
        else:
            raise KeyError('method "%s" not support!' % self.requestMethod)
        if self.checkPoint:
            try:
                params = self.checkPoint.split('=')
            except TypeError:
                logger.error('checkPoint input error!')
                raise TypeError('checkPoint input error : %s' % self.checkPoint)
            value = str(self.response[params[0]])
            if value != params[1]:
                self.result = 'Fail'
                logger.info('No.%s check point "%s" not found ' % (self.no, self.checkPoint))
                return self.response
            elif value == params[1]:
                self.result = 'Pass'
        else:
            self.result = 'Pass'
        #   判断是否存在关联,如果有且格式正确则将执行后的参数写入配置文件
        #   例如:$token=token,则将response中的token值存入到配置文件中
        if self.relation:
            try:
                l = self.relation.split(',')
            except TypeError:
                logger.error('relation input error!')
                raise TypeError('relation input error : %s' % self.relation)
            for i in l:
                try:
                    params = i[1:].split('=')
                except TypeError:
                    logger.error('relation input error!')
                    raise TypeError('relation input error : %s' % i)
                data = self.y.data[0]
                #   response['data']根据项目不同可能略有差异
                #   此处防止response中想要获取的值为空,产生异常
                if self.response['data'][params[0]] == None:return self.response
                try:
                    data[params[1]] = self.response['data'][params[0]].encode('utf-8')
                except AttributeError:
                    data[params[1]] = self.response['data'][params[0]]
                #   将变量写入yaml文件
                self.y.data = yaml.dump(data, default_flow_style=False)
        return self.response

* 三、编写测试用例

都写好啦,终于可以编写接口测试用例了!11,和10列我用来存储执行用例的情况,一个是检查点是否成功,另一个是响应内容。

#!/usr/bin/env python
#coding=utf-8

import unittest
import os
from utils.file_reader import ExcelReader
from utils.log import logger
from utils.config import Config, DATA_PATH
from utils.interface_case import InterfaceCase


class InterfaceTest001(unittest.TestCase):
    excel_path = os.path.join(DATA_PATH, Config().get('data')['interface_case_list'])
    e = ExcelReader(excel_path, 0, True)
    case_list = e.data

    def test_func(self):
        for i in self.case_list:
            #   运行用例前,将xlsx文件中执行结果以及用例执行情况还原
            self.e.set_value(int('%d' % i['No.']), 11, 'Na')
            self.e.set_value(int('%d' % i['No.']), 10, '')
            t = InterfaceCase(i)
            #   是否执行该条用例
            if t.active == 'yes':
                response = t.run()
                logger.info(t)
                #   将用例执行结果以及用例执行情况写入xlsx
                self.e.set_value(t.no, 10, response['msg'])
                self.e.set_value(t.no, 11, t.result)


if __name__ == '__main__':
    #   运行测试用例
    unittest.main()

事后一些小小的想法

虽然刚开始的时候觉得落叶归根这种excel存储测试用例的方法实在是太赞了,但是写完之后发现了很多的局限性。比如这样写,我就很难用灰蓝博客中介绍的生成html报告的方法,只能看自己输出的日志文件。

项目开始的时候,我想用这个框架做接口测试,但是发现这个测试框架根本就不够灵活。比如我要做一个登录次数上限的用例,我就不能用这个测试框架,必须要自己重新写测试类。

还有一个特别困扰的问题,师父希望接口测试的脚本更加的灵活。灵活到,我给别人发一个脚本,他F5就可以直接运行。而不是这个框架这样,需要其他的测试员去读懂这个框架。读懂之后还要配置这个框架需要的环境。

总的来说,动手编写这个框架还是很开心的,提升了自己编写python脚本的能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值