jinja2 quickly realizes the test report of customized robot framework

1, Background

The RF result report can facilitate us to view each use case set and the execution result statistics of use cases. However, some projects involve the comparison of some data. We hope to see the data intuitively. The original test report can not meet this demand.

  • Native Report

  • Project requirements report format

2, Solution

2.1 flow chart

  • Parse output XML to obtain the relevant information and execution results of the use case
  • Obtain the data to be displayed through API and database
  • Encapsulate the data of the above two steps into a data list to facilitate template rendering later
  • Draw HTML report template according to requirements
  • Perform data filling and rendering on the template to generate a report file
  • Mail sending report
2.2 output.xml parsing

We can analyze the RF output by analyzing the test case related information and operation results XML file

2.2.1 obtain statistics of use case operation

import xml.dom.minidom
import xml.etree.ElementTree

# Open xml document
dom = xml.dom.minidom.parse('E:\\robot\\fightdata_yuce\\results\\output.xml')
root2 = xml.etree.ElementTree.parse('E:\\robot\\fightdata_yuce\\results\\output.xml')

# Get document element object
root = dom.documentElement
total = root.getElementsByTagName('total');
total_len = len(total)
# total number of stat nodes
total2 = root2.getiterator("total")
total_stat_num = len(total2[total_len-1].getchildren())
statlist = root.getElementsByTagName('stat');

def get_total_statistics():
    list = []
    for i in range(0,total_stat_num):
        d = dict()
        d['fail'] = int(statlist[i].getAttribute("fail"))#Number of failed cases
        d['pass'] = int(statlist[i].getAttribute("pass"))#Number of successful cases
        d['total'] = d['fail']+d['pass']#Total number of use cases
        d['percent'] = ('{:.2%}'.format(d['pass'] / d['total']))#Percentage of use cases
        list.append(d)
    return list
2.2.2 obtaining use case information
  • Organizational structure of use cases
  • Obtain use case information and execution results
    The use case structure is multiple suites, with four cases under each suite
import xml.dom.minidom
import xml.etree.ElementTree

# Open xml document
dom = xml.dom.minidom.parse('E:\\robot\\xxx\\results\\output.xml')
root2 = xml.etree.ElementTree.parse('E:\\robot\\xxx\\results\\output.xml')
tree3=root2.getroot()
# Get child nodes under suite
def getcase():
    casedict = {}
    testlist2 = []
    for elem in tree3.iterfind('suite/suite'):
        a = elem.attrib
        suitedict = {}
        testlist2.append(suitedict) #Each use case set is stored in the list
        testlist = []
        suitename = a['name']#Get the name of the use case combination
        for test in elem.iter(tag='test'):
            b=test.attrib
            for data in test.iterfind('status'):
                 casename = b['name'] #Get the name of the use case
                 c=data.attrib
                 status=c['status'] #Get the execution result of each use case
                 casedict['casename'] = casename #Use case names are stored in the dictionary
                 casedict['status'] = status #The use case execution results are stored in the dictionary
            testlist.append(casedict) #The name and execution result of each use case are stored in the list as a dictionary
            casedict = {}
            suitedict['suitename']=suitename
            suitedict['test']=testlist
    return testlist2   #The final return is [{'suitename':'xxx ',' test ': [{' casename ':'01 xxx', 'status':'pass'},
                                      #{'casename': '02 xxx', 'status': 'PASS'}
2.3 data filling

Through the previous acquisition and filling into the template of jinja2, another html file with data will be generated

from jinja2 import Environment, FileSystemLoader
import parsexml


def generate_html(data):
    env = Environment(loader=FileSystemLoader('./'))   # Load template
    template = env.get_template('report.html')
    # template.stream(body).dump('result.html', 'utf-8')
    data=parsexml.get_total_statistics()#Get the use case statistics of the parsed xml
    data2=parsexml.getcase()#Obtain test case information and execution results
    with open("result.html", 'w',encoding='utf-8') as fout:
        html_content = template.render(data=data,data2=data2)
        fout.write(html_content)    #  Generate html by writing template
2.4 jinja2 template introduction

The principle of jinja2 template is to create an html template file first, and then render the data to the template file to generate a rendered html file, which will display the filled data

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Zhanwa profit center index automation test report</title>
</head>
<body>
    <div style="width:100%;float:left">
                    <table cellspacing="0" cellpadding="4" border="1" align="left">
                        <thead>
                            <tr bgcolor="#F3F3F3">
                                <td style="text-align:center" colspan="9"><b>Automatic detection of daily report of zhanwa profit center</b></td>
                            </tr>
                           <!-- <tr>
                                <td bgcolor="#F3f3f3 "style =" width: 80px "> < b > detailed report:</b></td>
                                <td colspan="8">
                                  <a href="${rooturl}${build.url}robot/report/report.html">Click to view report details</a>
                                </td>
                            </tr>-->
                            <tr>
                                <td bgcolor="#F3f3 "style =" width: 100px "> < b > indicator prediction rules:</b></td>
                                <td colspan="8">
                                 <a href="https://docs. qq. COM / sheet / dt1zbzhdgdxjkvwfl "> Click to view indicator prediction rules</a>
                                </td>
                            </tr>
                            <!--The statistics of use cases are defined here-->
                            <tr bgcolor="#F3F3F3">
                                <td><b>Total number of use cases</b></td>
                                <td><b>adopt</b></td>
                                <td style="width:60px"><b>Fail</b></td>
                                <td colspan="6"><b>Pass rate</b></td>
                            </tr>
                            <!--Pass 2.3 The data obtained by rendering is filled here-->
                            <tr>
                                <td>{{data['total']}}</td>
                                <td><b><span style="color:#66CC00">{{data['pass']}}</span></b></td>
                                <td><b><span style="color:#FF3333">{{data['fail']}}</span></b></td>
                                <td colspan="6">{{data['percent']}}</td>
                            </tr>
                            <!-- The header information of the use case is defined here-->
                            <tr bgcolor="#F3F3F3">
                                <td colspan="2"><b>Test Name</b></td>
                                <td><b>results of enforcement</b></td>
                                <td><b>Upper prediction limit</b></td>
                                 <td><b>Lower prediction limit</b></td>
                                 <td><b>Estimate</b></td>
                                 <td><b>actual value</b></td>
                                <td><b>Difference(forecast-actual)</b></td>
                                <td><b>Percentage difference</b></td>
                            </tr>
                        </thead>
                        <tbody>
                        <!--Pass 2.3 Data obtained by rendering data2,That is, the use case information data is filled in here, because it involves multiple suite,
                        each suite There are multiple under case,Here, take each through two layers of circulation case Information about -->
                        {% for data in data2 %}
                         <tr>
                                <td colspan="9"><b>{{data['suitename']}}</b></td>
                            </tr>

                        <tr>
                             {% for c2 in data['test'] %}
                                <td colspan="2">{{c2['casename']}}</td>
                                {% if c2['status']=='PASS' %}
                                    <td><b><span style="color:#66CC00">{{c2['status']}}</span></b></td>
                                    {% else %}
                                    <td><b><span style="color:#FF3333" >{{c2['status']}}</span></b></td>
                                {% endif %}
                                <td>{{c2['max']}}</td>
                             <td>{{c2['min']}}</td>
                             {% if c2['casename']=='01 GMV' %}
                                    <td>{{c2['yhat']}}</td>
                                    {% else %}
                                    <td>--</td>
                            {% endif %}
                             <td>{{c2['real']}}</td>
                             {% if c2['casename']=='01 GMV' %}
                                    <td>{{c2['reduce']}}</td>
                                    {% else %}
                                    <td>--</td>
                            {% endif %}
                            {% if c2['casename']=='01 GMV' %}
                                    <td>{{c2['percent']}}</td>
                                    {% else %}
                                    <td>--</td>
                            {% endif %}
                            </tr>
                        {% endfor %}
                        {% endfor %}
                        </tbody>
                    </table>
</body>
</html>
2.5 sending mail

Send the html file with data generated by the above rendering as a test report

# !/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib, time, os
from email.mime.text import MIMEText
from email.header import Header
import generate

def send_mail_html(file):
    sender = 'ccc@fulu.com' #Sender
    mail_to =['aa@fulu.com','bb@fulu.com] #addressee
    t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())  #Get current time
    subject = 'Zhanwa profit center index automation test report' + t  #Mail subject
    smtpserver = 'smtp.qiye.aliyun.com' #Sending server address
    username = 'cc@fulu.com' #user name
    password = '123456' #password
    f = open(file, 'rb')
    mail_body = f.read()
    f.close()


    msg = MIMEText(mail_body, _subtype='html', _charset='utf-8')
    msg['Subject'] = Header(subject, 'utf-8')
    msg['From'] = sender
    msg['To'] = ";".join(mail_to)

    try:
        smtp = smtplib.SMTP()
        smtp.connect(smtpserver)
        smtp.login(username, password)
        smtp.sendmail(sender, mail_to, msg.as_string())
    except:
        print("Mail sending failed!")
    else:
        print("Mail sent successfully!")
    finally:
        smtp.quit()

def result():
    file = 'result.html' #Rendered html report file
    result = {}
    generate.generate_html(result)
    send_mail_html(file)

Email display results

3, Review the whole implementation process

When we first received the demand, we tried to find relevant implementation schemes on the Internet. The RF plug-in provided by jenkins can analyze the report and send e-mail. In the first version, we pushed the report template
For the first edition of the report, please refer to this blog post https://blog.csdn.net/qq_38317509/article/details/81316940

When you need to display the specific data information of each use case, you also try to modify the value of the template file, and check the source code implementation of the plug-in. It is found that this personalized data value cannot be obtained. The source code only returns the use case information, execution status and execution time, as well as the msg at the time of failure. Can you make an article through the keyword msg, That is, the data to be viewed is printed and displayed through msg. However, it is found that the use case is displayed only when the case fails, not when it succeeds, and the data is displayed in log, so the view is not so clear

Based on the above situation, we found the package jinja2, so we gave up jenkins' plug-in, customized the report template, and then filled in the data. In this way, the flexibility is greatly improved, and the subsequent personalized requirements are convenient for customization and development. At present, we still rely on jenkins to trigger the test task. We hope to container the RF part. Do you have relevant practice? Welcome guidance~

Ferro ICH · quality assurance department Fu Xiaolong

Tags: Testing

Posted by damdempsel on Thu, 19 May 2022 07:04:29 +0300