RobotFramework测试用例的自行解析

RobotFramework框架提供了很多有用的开放API供我们使用,具体可以参考RobotFramework官方网站的开放API。

其中,下面的类和方法是比较常用的:

  • logger module for test libraries’ logging purposes.
  • deco module with decorators test libraries can utilize.
  • TestCaseFile, TestDataDirectory, and ResourceFile classes for parsing test data files and directories. In addition, a convenience factory method TestData() creates either TestCaseFileor TestDataDirectory objects based on the input.
  • TestSuite class for creating executable test suites programmatically and TestSuiteBuilderclass for creating such suites based on existing test data on the file system.
  • SuiteVisitor abstract class for processing testdata before execution. This can be used as a base for implementing a pre-run modifier that is taken into use with --prerunmodifiercommandline option.
  • ExecutionResult() factory method for reading execution results from XML output files andResultVisitor abstract class to ease further processing the results. ResultVisitor can also be used as a base for pre-Rebot modifier that is taken into use with --prerebotmodifiercommandline option.
  • ResultWriter class for writing reports, logs, XML outputs, and XUnit files. Can write results based on XML outputs on the file system, as well as based on the result objects returned by the ExecutionResult() or an executed TestSuite.

下面是一个根据RobotFramework测试运行结果的XML文件解析RobotFramework测试用例数据的示例代码:

from xml.etree import ElementTree as xet
import numpy as np
import pandas as pd
import pylab
import os
from datetime import datetime
import matplotlib
matplotlib.use("Agg")

from matplotlib import pyplot as plt

if os.name == 'posix':
    matplotlib.rcParams['font.sans-serif'] = ['SimHei']
if os.name == 'nt':
    matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
plt.rcParams['savefig.dpi'] = 200
plt.rcParams['figure.dpi'] = 200


def plot_pie(sizes, labels, title, explode, file_name_to_save):

    fig, ax = plt.subplots()
    ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', shadow=False, startangle=0)
    ax.axis('equal')
    plt.title(title, fontsize=20, fontweight='bold')
    plt.legend()
    plt.savefig(file_name_to_save, dpi=200)


def plot_bar(cat1, cat2, labels, xlabel, ylabel, title, max_y, filename, width=0.35):
    cat1 = cat1
    cat2 = cat2
    labels = labels
    x = np.arange(len(cat1))
    plt.bar(x, height=cat1, width=width, alpha=0.8, color="green", label="Passed")
    plt.bar(x, height=cat2, width=width, color="red", label="Failed", bottom=cat1)

    plt.ylim(0, max_y)

    plt.ylabel(xlabel)
    plt.xlabel(ylabel)

    plt.xticks(x, labels)
    pylab.xticks(rotation=45)

    plt.title(title)
    plt.legend()
    plt.savefig(filename, dpi=200)


def parse_rfoutput_total(root):
    for sub_child in root:
        if sub_child.text == "Critical Tests":
            labels = "Passed", "Failed"
            sizes = [sub_child.attrib['pass'], sub_child.attrib['fail']]
            explode = (0, 0.2)
            title = "Critical Tests Execute Result"
            filename = "./statistics/Critical_Tests_info.png"
            plot_pie(sizes, labels=labels, title=title, explode=explode, file_name_to_save=filename)

        if sub_child.text == "All Tests":
            labels = "Passed", "Failed"
            sizes = [sub_child.attrib['pass'], sub_child.attrib['fail']]
            explode = (0, 0.2)
            title = "All Tests Execute Result"
            filename = "./statistics/All_Tests_info.png"
            plot_pie(sizes, labels=labels, title=title, explode=explode, file_name_to_save=filename)


def parse_statistics_by_suite(root):
    labels = list()
    passed = list()
    failed = list()
    max_num = list()
    for sub_child in root:
        labels.append(sub_child.text)
        passed.append(int(sub_child.attrib['pass']))
        failed.append(int(sub_child.attrib['fail']))
        max_num.append(int(sub_child.attrib['pass']) + int(sub_child.attrib['fail']))
    print(max_num)
    plot_bar(cat1=passed, cat2=failed,
             labels=labels,
             xlabel="测试套件", ylabel="用例数量",
             title="Statistic By Suite", max_y=max(max_num),
             filename="./statistics/Statistic_By_Suite.png"
             )


def parse_statistic_by_casetags(root):
    labels = list()
    passed = list()
    failed = list()
    max_num = list()
    for sub_child in root:
        labels.append(sub_child.text)
        passed.append(int(sub_child.attrib['pass']))
        failed.append(int(sub_child.attrib['fail']))
        max_num.append(int(sub_child.attrib['pass']) + int(sub_child.attrib['fail']))
    print(max_num)
    plot_bar(cat1=passed, cat2=failed,
             labels=labels,
             xlabel="TestCase Tag", ylabel="Case Number",
             title="Statistic By Tag", max_y=max(max_num),
             filename="./statistics/Statistic_By_Tag.png"
             )


def parse_statistics_info(root):
    for child in root:
        if child.tag == "total":
            parse_rfoutput_total(child)

        if child.tag == "tag":
            parse_statistic_by_casetags(child)

        if child.tag == "suite":
            parse_statistics_by_suite(child)


def parse_testcase_tree(root):
    case_info = {
        'CaseId': root.attrib['id'],
        'CaseName': root.attrib['name'],
        'CaseTags': set(),
        'CaseStatus': None,
        'CaseStartTime': None,
        'CaseEndTime': None,
        'CaseElapsedTime': None
    }

    for child in root:
        if child.tag == "tags":
            for sub in child:
                case_info['CaseTags'].add(sub.text.strip())
        if child.tag == "status":
            case_info['CaseStatus'] = child.attrib['status']
            case_info['CaseStartTime'] = child.attrib['starttime']
            case_info['CaseEndTime'] = child.attrib['endtime']

            case_running_elapsed_time = datetime.strptime(
                case_info['CaseEndTime'], "%Y%m%d %H:%M:%S.%f"
            ) - datetime.strptime(
                case_info['CaseStartTime'], "%Y%m%d %H:%M:%S.%f"
            )
            case_info['CaseElapsedTime'] = case_running_elapsed_time.total_seconds()

    return case_info


def parse_rf_output(root, testcasetable):
    if root.tag == 'test':
        testcasetable.append(parse_testcase_tree(root))
    elif root.tag == "statistics":
        parse_statistics_info(root)
    else:
        for child in root:
            parse_rf_output(child, testcasetable)


def get_latest_test_run_ouput(sourcedir='../TestResult/', sort_by='ctime'):
    if os.path.isdir(sourcedir):
        if len(os.listdir(sourcedir)) == 0:
            raise FileNotFoundError("指定的目录中没有文件。")
        else:
            os.chdir(sourcedir)
            files = [file for file in os.listdir(sourcedir) if \
                     file.startswith('output_') and file.endswith('.xml')]
            if sort_by == 'ctime':
                files.sort(
                    key=lambda f: os.path.getctime(sourcedir + os.sep + f)
                )
            elif sort_by == 'atime':
                files.sort(
                    key=lambda f: os.path.getatime(sourcedir + os.sep + f)
                )
            elif sort_by == 'mtime':
                files.sort(
                    key=lambda f: os.path.getmtime(sourcedir + os.sep + f)
                )
            else:
                raise ValueError("排序方式不正确。")
            return files[-1]
    else:
        raise NotADirectoryError("无此目录。")


if __name__ == '__main__':
    testcasetable = list()
    # file = get_latest_test_run_ouput(sourcedir="../TestResult")
    xmltree = xet.parse("./output_183.xml")
    root = xmltree.getroot()
    parse_rf_output(root, testcasetable)
    dt = pd.DataFrame(testcasetable)
    dt.to_excel("./statistics/testcases.xlsx")

你可能感兴趣的:(RobotFramework测试用例的自行解析)