HOW TO 提取 Cucumber Failed 的 Scenarios log 信息

openpyxl format cells 格式化单元格 文中介绍了自动化 triage report 格式化问题,信息提取部分将在本文中介绍。

背景:

还是在日常 triage API automation 时遇到一些痛点,基于Cucumber BDD 测试框架,每天 smoke 加 regression 多达 1 万多的 cases,怎样提高 triage 效率,怎么减少人力工作,做为一个爱思考的 QA 都必须想办法解决,哈哈!

怎么才能只获取 Failed 的 Scenarios 信息,还能将 cucumber report 和 REST log结合起来,统统放在一个表格里就能获取所有资源信息呢,这样就不用东找西找,还能生成一个漂亮的 triage report,将人力工作统统自动化。

成果:

很早就有这个想法,等待一个时机就开始行动了,并且最终实现了,也就 400 行 Python 脚本就能解决的问题。效果如下,每个 fail 的 Scenario details 信息,对应的 REST log 文件 link,每个 Feature 的汇总信息,对应的 Cucumber Report link,是不是有点酷,反正我是很有成就感了,也得到了小伙伴们的称赞,大大提高了triage效率。

Failed Scenarios Details:
在这里插入图片描述

Features Summary Report:
在这里插入图片描述

实现依据:

以上信息的提取主要结合 cucumber json report, REST log 信息和第三方maven-cucumber-reporting 产生的 report。其实 maven-cucumber-reporting 产生的 report 也是基于 cucumber json report 生成的。

cucumber json report 是将 cucumber feature files 里每个 scenarios 的运行结果以 json 的形式输出。只需在 Cucumber Runner Class 中配置即可产生这样一个json report:

format = {"json:target/cucumber.json"}

下面就来解读一下这个json文件:
每个{ }就是一个feature, 有feature name, feature uri信息,下图中就运行了 8 个feature files
elements 里就是这个 feature 的所有 scenarios
在这里插入图片描述

element 中每个 { } 就是一个 scenario 执行信息, scenario line, scenaio name, scenario uri,下图这个 feature 有 3 个 scenarios,
在这里插入图片描述

Step 中的元素就是这个 scnario 中具体的 steps 执行情况,
每个 { } 就是一个 step,有 step name, step line, step result(status, error messages)
在这里插入图片描述

Scenarios 的运行信息都可以通过这个 json file 获取,至于 REST log 和 feature report 信息就得结合 feature uri, step uri 找到对应的 mapping 关系就可以了。

代码

就只贴提取信息代码,格式化代码参考 openpyxl format cells 格式化单元格

定义 Scenario dataset

class Scenario_Dataset:
    def __init__(self,feature_name, feature_rest_log_dir,scenario_name, scenario_line, step_name, step_line, step_error_message, step_error_type=None, step_rest_error_message=None, step_rest_error_url=None):
        self.feature_name = feature_name
        self.feature_rest_log_dir = feature_rest_log_dir
        self.scenario_name = scenario_name
        self.scenario_line = scenario_line
        self.step_name = step_name
        self.step_line = step_line
        self.step_error_message = step_error_message
        self.step_rest_error_message = step_rest_error_message
        self.step_rest_error_url = step_rest_error_url
        self.step_error_type = step_error_type

    def set_rest_error_message(self, step_rest_error_message):
        self.step_rest_error_message = step_rest_error_message

    def set_rest_error_url(self, step_rest_error_url):
        self.step_rest_error_url = step_rest_error_url

定义 Feature dataset

class Feature_Dataset:
    def __init__(self, feature_name, total_passed_scenarios, total_failed_scenarios, total_scenarios, error_type_set, feature_report_url):
        self.feature_name = feature_name
        self.total_passed_scenarios = total_passed_scenarios
        self.total_failed_scenarios = total_failed_scenarios
        self.total_scenarios = total_scenarios
        self.error_type_set = error_type_set
        self.feature_report_url = feature_report_url 

提取 Feature info

def read_cucumber_json(cucumber_file_name):
    with io.open(cucumber_file_name,'rt',encoding='utf-8') as f:
        data = json.load(f)
    return data
def extract_feature_infor_from_cucumber_json(cucumber_file_name):
    data = read_cucumber_json(cucumber_file_name)
    feature_error_list = []
    features_summary_list = []
    for item in data:
        feature_dataset, scenario_error_list = extract_scenario_info(item)
        if len(scenario_error_list) > 0:
            feature_error_list.append(scenario_error_list)
            features_summary_list.append(feature_dataset)
    return features_summary_list, feature_error_list

提取 scenario info

def extract_scenario_info(item):
    scenario_error_list = []
    feature_dataset = None
    error_type_set = dict()
    elements = item["elements"]
    if elements[0]["type"] == 'background':
       total_scenarios = len(elements) - 1
    else:
        total_scenarios = len(elements)
    total_failed_scenarios = 0
    feature_name = item['name']
    feature_rest_log_dir = get_feature_log_dir(item['uri'])
    feature_report_url = ''
    for element in elements:
        scenario_dataset = extract_steps_info(feature_name, feature_rest_log_dir, element)
        if scenario_dataset is not None:
            total_failed_scenarios += 1
            if scenario_dataset.step_error_type in error_type_set:
                error_type_set[scenario_dataset.step_error_type] += 1
            else:
                error_type_set[scenario_dataset.step_error_type] = 1
            scenario_error_list.append(scenario_dataset)
    if total_failed_scenarios > 0:
        feature_report_url = get_report_file_path(item['uri'].replace('/', '-').replace('.','-'))
        feature_dataset = Feature_Dataset(feature_name, total_scenarios-total_failed_scenarios, total_failed_scenarios, total_scenarios, str(error_type_set), feature_report_url)
    return feature_dataset, scenario_error_list

提取 step info

def extract_steps_info(feature_name, feature_rest_log_dir,element):
    scenario_name = element['name']
    scenario_line = element['line']
    steps = element['steps']
    step_error_type = None
    scenario_dataset = None
    for step in steps:
        step_result = step['result']
        step_status = step_result['status']
        if step_status == 'failed':
            step_error_message = step_result['error_message']
            step_line = step['line']
            step_name = step['name']
            end_index = step_error_message.find('at ')
            step_error_message = step_error_message[:end_index].strip()
            if step_error_message.find('Expected status code') > 0:
                status_code_end_index = step_error_message.find('>.')
                step_error_type = int(step_error_message[status_code_end_index - 3:status_code_end_index])
            elif step_error_message.find('UnknownHostException') >= 0:
                 step_error_type = 'Name or service not known'
            else:
                step_error_type = 'the response does not match the expected'
            scenario_dataset = Scenario_Dataset(feature_name, feature_rest_log_dir, scenario_name, scenario_line, step_name, step_line, step_error_message, step_error_type=step_error_type)  
            break   
    return scenario_dataset

如果要运行在 jenkins上,还得结合这些变量找到对应的文件关系。

WORKSPACE = os.getenv('WORKSPACE')
BUILD_URL = os.getenv('BUILD_URL')
ARTIFACT_URL = join(BUILD_URL, 'artifact')
JOB_NAME = os.getenv('JOB_BASE_NAME')
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值