测试开发进阶(三十八)

用例模块

需要使用httprunner来进行用例的执行与报告的生成

所以我们需要生成一个yaml用例文件,再执行它

@action(methods=['post'], detail=True)	
def run(self, request, *args, **kwargs):	
    instance = self.get_object()	
    serializer = self.get_serializer(instance, data=request.data)	
    serializer.is_valid(raise_exception=True)	
    datas = serializer.validated_data	
    env_id = datas.get('env_id')	
    testcase_dir_path = os.path.join(settings.SUITES_DIR, datetime.strftime(datetime.now(), '%Y%m%d%H%M%S%f'))	
    if not os.path.exists(testcase_dir_path):	
        os.mkdir(testcase_dir_path)	
    env = Envs.objects.filter(id=env_id, is_delete=False).first()	
    # 生成yaml用例文件	
    common.generate_testcase_files(instance, env, testcase_dir_path)	
    # 运行用例	
    return common.run_testcase(instance, testcase_dir_path)

生成yaml用例

官方文档有一个例子:

- config:	
    name: testcase description	
    variables: {}	
- test:	
    name: /api/get-token	
    request:	
        headers:	
            Content-Type: application/json	
            User-Agent: python-requests/2.18.4	
            app_version: 2.8.6	
            device_sn: FwgRiO7CNA50DSU	
            os_platform: ios	
        json:	
            sign: 9c0c7e51c91ae963c833a4ccbab8d683c4a90c98	
        method: POST	
        url: http://127.0.0.1:5000/api/get-token	
    extract:	
        token: content.token	
    validate:	
        - eq: [status_code, 200]	
        - eq: [headers.Content-Type, application/json]	
        - eq: [content.success, true]	
- test:	
    name: /api/users/1000	
    request:	
        headers:	
            Content-Type: application/json	
            User-Agent: python-requests/2.18.4	
            device_sn: FwgRiO7CNA50DSU	
            token: $token	
        json:	
            name: user1	
            password: '123456'	
        method: POST	
        url: http://127.0.0.1:5000/api/users/1000	
    validate:	
        - eq: [status_code, 201]	
        - eq: [headers.Content-Type, application/json]	
        - eq: [content.success, true]	
        - eq: [content.msg, user created successfully.]

上述一个yaml对应的json格式为:

[	
  {	
    "config": {	
      "name": "testcase description",	
      "request": {	
        "base_url": "",	
        "headers": {	
          "User-Agent": "python-requests/2.18.4"	
        }	
      },	
      "variables": [],	
      "output": ["token"],	
      "path": "/abs-path/to/demo-quickstart-2.yml",	
      "refs": {	
        "env": {},	
        "debugtalk": {	
          "variables": {	
            "SECRET_KEY": "DebugTalk"	
          },	
          "functions": {	
            "gen_random_string": <function gen_random_string at 0x108596268>,	
            "get_sign": <function get_sign at 0x1085962f0>,	
            "get_user_id": <function get_user_id at 0x108596378>,	
            "get_account": <function get_account at 0x108596400>,	
            "get_os_platform": <function get_os_platform at 0x108596488>	
          }	
        },	
        "def-api": {},	
        "def-testcase": {}	
      }	
    },	
    "teststeps": [	
      {	
        "name": "/api/get-token",	
        "request": {	
          "url": "http://127.0.0.1:5000/api/get-token",	
          "method": "POST",	
          "headers": {"Content-Type": "application/json", "app_version": "2.8.6", "device_sn": "FwgRiO7CNA50DSU", "os_platform": "ios", "user_agent": "iOS/10.3"},	
          "json": {"sign": "9c0c7e51c91ae963c833a4ccbab8d683c4a90c98"}	
        },	
        "extract": [	
          {"token": "content.token"}	
        ],	
        "validate": [	
          {"eq": ["status_code", 200]},	
          {"eq": ["headers.Content-Type", "application/json"]},	
          {"eq": ["content.success", true]}	
        ]	
      },	
      {	
        "name": "/api/users/1000",	
        "request": {"url": "http://127.0.0.1:5000/api/users/1000", "method": "POST", "headers": {"Content-Type": "application/json", "device_sn": "FwgRiO7CNA50DSU", "token": "$token"},	
        "json": {"name": "user1", "password": "123456"}},	
        "validate": [	
          {"eq": ["status_code", 201]},	
          {"eq": ["headers.Content-Type", "application/json"]},	
          {"eq": ["content.success", true]},	
          {"eq": ["content.msg", "user created successfully."]}	
        ]	
      }	
    ]	
  },	
  {...} # another testcase	
]

所以我们需要通过一个函数将已有的接口,环境,配置写入一份yaml中

将对应的 debugtalk.py存放在yaml文件附近

def generate_testcase_files(instance, env, testcase_dir_path):	
    testcase_list = []	
    config = {	
        'config': {	
            'name': instance.name,	
            'request': {	
                'base_url': env.base_url if env else ''	
            }	
        }	
    }	
    testcase_list.append(config)	
    include = json.loads(instance.include, encoding='utf8')	
    request = json.loads(instance.request, encoding='utf8')	
    interface_name = instance.interface.name	
    project_name = instance.interface.project.name	
    testcase_dir_path = os.path.join(testcase_dir_path, project_name)	
    if not os.path.exists(testcase_dir_path):	
        os.mkdir(testcase_dir_path)	
        debugtalk_obj = DebugTalks.objects.filter(is_delete=False, project__name=project_name).first()	
        if debugtalk_obj:	
            debugtalk = debugtalk_obj.debugtalk	
        else:	
            debugtalk = ''	
        with open(os.path.join(testcase_dir_path, 'debugtalk.py'),	
                  mode='w', encoding='utf8') as one_file:	
            one_file.write(debugtalk)	
        testcase_dir_path = os.path.join(testcase_dir_path, interface_name)	
        if not os.path.exists(testcase_dir_path):	
            os.mkdir(testcase_dir_path)	
        if 'config' in include:	
            config_id = include.get('config')	
            config_obj = Configures.objects.filter(is_delete=False, id=config_id).first()	
            if config_obj:	
                config_request = json.loads(config_obj.request, encoding='utf8')	
                config_request.get('config').get('request').setdefault('base_url', env.base_url)	
                config_request['config']['name'] = instance.name	
                testcase_list[0] = config_request	
            if 'testcases' in include:	
                for t_id in include.get('testcases'):	
                    testcase_obj = Testcases.objects.filter(is_delete=False, id=t_id).first()	
                    if testcase_obj:	
                        try:	
                            testcase_request = json.loads(testcase_obj.request, encoding='utf8')	
                        except Exception as e:	
                            testcase_request = ''	
                    else:	
                        testcase_list.append(testcase_request)	
    testcase_list.append(request)	
    with open(os.path.join(testcase_dir_path, instance.name + '.yml'), 'w', encoding='utf8') as one_file:	
        yaml.dump(testcase_list, one_file, allow_unicode=True)

执行yaml文件

640?wx_fmt=jpeg

从https://cn.httprunner.org/development/dev-api/可以看出,我们可以通过传入yaml路径来执行测试

def run_testcase(instance, testcase_dir_path):	
    """	
    运行用例	
    :return:	
    :param instance: 实例	
    :param testcase_dir_path: 用例根目录路径	
    :return dict	
    """	
    runner = HttpRunner()	
    # runner.run(testcase_dir_path)	
    try:	
        runner.run(testcase_dir_path)	
    except ParamsError:	
        logger.error("用例参数有误")	
        data = {	
            "msg": "用例参数有误"	
        }	
        return Response(data, status=400)	
    runner.summary = timestamp_to_datetime(runner.summary, type=False)	
    try:	
        report_name = instance.name	
    except Exception as e:	
        report_name = '被遗弃的报告' + '-' + datetime.strftime(datetime.now(), '%Y%m%d%H%M%S')	
    report_id = create_report(runner, report_name=report_name)	
    data_dict = {	
        "id": report_id	
    }	
    return Response(data_dict, status=status.HTTP_201_CREATED)

报告中的时间格式需要进行调整

def create_report(runner, report_name=None):	
    """	
    创建测试报告	
    :param runner:	
    :param report_name:	
    :return:	
    """	
    time_stamp = int(runner.summary["time"]["start_at"])	
    start_datetime = datetime.fromtimestamp(time_stamp).strftime('%Y-%m-%d %H:%M:%S')	
    runner.summary['time']['start_datetime'] = start_datetime	
    # duration保留3位小数	
    runner.summary['time']['duration'] = round(runner.summary['time']['duration'], 3)	
    report_name = report_name if report_name else start_datetime	
    runner.summary['html_report_name'] = report_name	
    for item in runner.summary['details']:	
        try:	
            for record in item['records']:	
                record['meta_data']['response']['content'] = record['meta_data']['response']['content']. \	
                    decode('utf-8')	
                record['meta_data']['response']['cookies'] = dict(record['meta_data']['response']['cookies'])	
                request_body = record['meta_data']['request']['body']	
                if isinstance(request_body, bytes):	
                    record['meta_data']['request']['body'] = request_body.decode('utf-8')	
        except Exception as e:	
            continue	
    summary = json.dumps(runner.summary, ensure_ascii=False)	
    report_name = report_name + '_' + datetime.strftime(datetime.now(), '%Y%m%d%H%M%S')	
    report_path = runner.gen_html_report(html_report_name=report_name)	
    with open(report_path, encoding='utf-8') as stream:	
        reports = stream.read()	
    test_report = {	
        'name': report_name,	
        'result': runner.summary.get('success'),	
        'success': runner.summary.get('stat').get('successes'),	
        'count': runner.summary.get('stat').get('testsRun'),	
        'html': reports,	
        'summary': summary	
    }	
    report_obj = Reports.objects.create(**test_report)	
    return report_obj.id

640?wx_fmt=jpeg

其他模块的执行与报告展示也调用了这两个函数

https://github.com/zx490336534/ApiTest

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值