HTMLReport应用之Unittest+Python+Selenium+HTMLReport项目自动化测试实战

举报
虫无涯 发表于 2023/03/14 13:18:23 2023/03/14
【摘要】 注意事项:【本文字数包含代码38175字,建议慢慢看~哈哈哈】1、以下仅为举例,具体以自身实际项目为准;2、以下内容重点是介绍HTMLReport的应用,并不是说明如何搭建框架;3、如果想了解框架内容,可移步博主有关测试框架的系列文章;4、写了一个用例,用例中没有加断言,只是为了生成测试报告,可以忽略;5、用例故意写错了3个,1个通过,是为了生成测试报告数据。 1 测试框架结构目录/脚本说明...

注意事项:
【本文字数包含代码38175字,建议慢慢看~哈哈哈】
1、以下仅为举例,具体以自身实际项目为准;
2、以下内容重点是介绍HTMLReport的应用,并不是说明如何搭建框架;
3、如果想了解框架内容,可移步博主有关测试框架的系列文章;
4、写了一个用例,用例中没有加断言,只是为了生成测试报告,可以忽略;
5、用例故意写错了3个,1个通过,是为了生成测试报告数据。

1 测试框架结构

在这里插入图片描述
在这里插入图片描述

目录/脚本 说明
common/reportOut.py 这是是用HTMLReport生成报告的
common/sendMain.py 这个是用来发邮件,本次演示可要可不要
report 是存放测试报告的,里边有3个文件,由HTMLReport自动生成
testcase 存放测试用例的
main.py 框架主入口

2 技术栈

技术 版本及说明
Python V3.x(本文为3.7)===编程语言支撑
Selenium V3.141.0 ===UI元素、控件的识别、定位,以及浏览器控制等
HTMLReport 生成Html测试报告
Unittest Python自带===自动化测试框架
Smtplib Python自带===邮件服务
email Python自带===邮件服务
os Python自带===系统模块
PyCharm Community 2020.2汉化版
操作系统 Windows10旗舰版64位
其它 后续补充

3 实现思路

  • 这里具体就是把原来生成HtmlTestRunner改为HTMLReport

3.1 使用HtmlTestRunner

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2022/3/7
# 文件名称:reportOut.py
# 作用:封装测试报告功能

import time
import unittest
from common import HTMLTestRunner    # 引入导入的报告模板


def report_out(test_dir, report_dir, name_project):
    '''
    :test_dir: 用例路径
    :report_dir : 报告路径
    :name_project : 项目名称=>用于报告命名及描述
    :return: 无
    '''

    now = time.strftime("%Y_%m_%d %H_%M_%S")
    discover = unittest.defaultTestLoader.discover(test_dir,pattern='test*.py')      # 加载测试用例
    report_name = report_dir + now + '-' + name_project+'_test_report.html'          # 报告名称
    with open(report_name,'wb') as f:                                                # 运行用例生成测试报告
        runner = HTMLTestRunner.HTMLTestRunner(stream=f,
                              title=name_project + 'WebUI Auto Testing Report',
                              description=(name_project + U"美多商城UI自动化功能回归测试"),
                              verbosity=2)
        runner.run(discover)
        f.close()

    """
    stream:要操作的文件;
    title:测试报告标题;
    description:报告描述;
    verbosity:报告级别。
    """

3.2 使用HTMLReport

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:reportOut.py
# 作用:封装测试报告功能

import time
import unittest
from HTMLReport import ddt, TestRunner, add_image, no_retry, retry


def report_out(test_dir, report_dir, name_project):
    '''
    :test_dir: 用例路径
    :report_dir : 报告路径
    :name_project : 项目名称=>用于报告命名及描述
    :return: 无
    '''

    now = time.strftime("%Y_%m_%d %H_%M_%S")
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')      # 加载测试用例
    # report_name = now + '-' + name_project + '_test_report.html'          # 报告名称
    test_runner = TestRunner(
        report_file_name=now,
        output_path=report_dir,
        title=name_project,
        description="关于HTMLReport的实际项目应用",
        thread_count=1,
        thread_start_wait=0.1,
        tries=0,
        delay=0,
        back_off=1,
        retry=True,
        sequential_execution=True,
        lang="cn"
    )
    test_runner.run(discover)

4 TestRunner参数说明

4.1 源码

class TestRunner(TemplateMixin, TestSuite):
    """测试执行器"""

    def __init__(self, report_file_name: str = None, log_file_name: str = None, output_path: str = None,
                 title: str = None, description: str = None, tries: int = 0, delay: float = 1, back_off: float = 1,
                 max_delay: float = 120, retry: bool = True, thread_count: int = 1, thread_start_wait: float = 0,
                 sequential_execution: bool = False, lang: str = "cn", image: bool = True, failed_image: bool = False):

4.2 参数说明

参数 说明
report_file_name 报告文件名,如果未赋值,将采用“test+时间戳”
log_file_name 日志文件名,如果未赋值,将采用报告文件名,如果报告文件名也没有,将采用“test+时间戳”
output_path 报告保存文件夹名,默认“report
title 报告标题,默认“测试报告”
description 报告描述,默认“无测试描述”
tries 重试次数
delay 重试延迟间隔,单位为 秒
back_off 扩展每次重试等待时间的乘数
max_delay 最大重试等待时间长度,单位为 秒
retry 如果为 True 表示所有用例遵循重试规则,False 只针对添加了 @retry 用例有效
thread_count 并发线程数量(无序执行测试),默认数量 1
thread_start_wait 各线程启动延迟,默认 0 s
sequential_execution 是否按照套件添加(addTests)顺序执行, 会等待一个addTests执行完成,再执行下一个,默认 False。如果用例中存在 tearDownClass ,建议设置为True否则 tearDownClass 将会在所有用例线程执行完后才会执行。
lang ("cn", "en") 支持中文与英文报告输出,默认采用中文
image 默认支持添加图片,False 放弃所有图片添加
failed_image true 只有失败才添加图片,成功用例添加的图片会被删除

5 框架代码

5.1 common/reportOut.py

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:reportOut.py
# 作用:封装测试报告功能

import time
import unittest
from HTMLReport import ddt, TestRunner, add_image, no_retry, retry


def report_out(test_dir, report_dir, name_project):
    '''
    :test_dir: 用例路径
    :report_dir : 报告路径
    :name_project : 项目名称=>用于报告命名及描述
    :return: 无
    '''

    now = time.strftime("%Y_%m_%d %H_%M_%S")
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')      # 加载测试用例
    # report_name = now + '-' + name_project + '_test_report.html'          # 报告名称
    test_runner = TestRunner(
        report_file_name=now,
        output_path=report_dir,
        title=name_project,
        description="关于HTMLReport的实际项目应用",
        thread_count=1,
        thread_start_wait=0.1,
        tries=0,
        delay=0,
        back_off=1,
        retry=True,
        sequential_execution=True,
        lang="cn"
    )
    test_runner.run(discover)

5.2 common/sendMain.py

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:sendMain.py
# 作用:封装邮件服务模块

import time
import smtplib
import getpass
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import email
import os


def send_main(file_path, mail_to='xxx@126.com'):
    mail_from = 'xxxx@126.com'
    f = open(file_path, 'rb')
    mail_body = f.read()
    f.close()

    # msg = email.MIMEMultipart.MIMEMultipart()
    msg = MIMEMultipart()

    # 构造MIMEBase对象做为文件附件内容并附加到根容器
    contype = 'application/octet-stream'
    maintype, subtype = contype.split('/', 1)

    # 读入文件内容并格式化
    data = open(file_path, 'rb')
    # file_msg = email.MIMEBase.MIMEBase(maintype, subtype)
    file_msg = MIMEBase(maintype, subtype)
    file_msg.set_payload(data.read())
    data.close()

    # email.Encoders.encode_base64(file_msg)
    encoders.encode_base64(file_msg)

    # 设置附件头
    basename = os.path.basename(file_path)
    file_msg.add_header('Content-Disposition', 'attachment', filename=basename)
    msg.attach(file_msg)
    print(u'msg 附件添加成功')

    msg1 = MIMEText(mail_body, "html", 'utf-8')
    msg.attach(msg1)

    if isinstance(mail_to, str):
        msg['To'] = mail_to
    else:
        msg['To'] = ','.join(mail_to)
    msg['From'] = mail_from
    msg['Subject'] = u'美多商城UI自动化功能回归测试'
    msg['date'] = time.strftime('%Y-%m-%d-%H_%M_%S')
    print(msg['date'])

    smtp = smtplib.SMTP()
    smtp.connect('smtp.126.com')
    smtp.login('xxx@126.com', 'xxx')  # 登录账号和密码(密码为之前申请的授权码)
    smtp.sendmail(mail_from, mail_to, msg.as_string())
    smtp.quit()
    print('email has send out !')

# if __name__=='__main__':
#     sendmain('../report/2017-08-18-10_18_57_result.html')

5.3 report

5.3.1 xxx.html

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="cn">

<head>
    <title>关于HTMLReport的实际项目应用</title>
    <meta name="generator" content="HTMLReport 刘士 2.3.1"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    

5.3.2 xxx.log

2023-03-07 17:15:55,952   25872     INFO test_runner.py(162) - 预计并发线程数:1
2023-03-07 17:15:58,071   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:15:58,183   23748     INFO result.py(73) - 开始测试: test_back_refresh (test_baidu.TestCase)
2023-03-07 17:15:58,329   23748    ERROR result.py(191) - 测试产生错误: test_back_refresh (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 43, in test_back_refresh
    self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:15:58,330   23748     INFO result.py(96) - 测试结束: test_back_refresh (test_baidu.TestCase)
2023-03-07 17:15:58,330   23748     INFO result.py(97) - 耗时: 0.14696788787841797
2023-03-07 17:15:58,330   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:15:58,434   23748     INFO result.py(73) - 开始测试: test_search (test_baidu.TestCase)
2023-03-07 17:16:00,445   23748    ERROR result.py(191) - 测试产生错误: test_search (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 26, in test_search
    self.driver.find_element_by_id("kw").send_keys("helloworld")  # 输入“helloworld”
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:16:00,445   23748     INFO result.py(96) - 测试结束: test_search (test_baidu.TestCase)
2023-03-07 17:16:00,445   23748     INFO result.py(97) - 耗时: 2.0103440284729004
2023-03-07 17:16:00,445   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:16:00,561   23748     INFO result.py(73) - 开始测试: test_serach_clear (test_baidu.TestCase)
2023-03-07 17:16:00,566   23748    ERROR result.py(191) - 测试产生错误: test_serach_clear (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 49, in test_serach_clear
    self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:16:00,567   23748     INFO result.py(96) - 测试结束: test_serach_clear (test_baidu.TestCase)
2023-03-07 17:16:00,567   23748     INFO result.py(97) - 耗时: 0.0060002803802490234
2023-03-07 17:16:00,567   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:16:00,670   23748     INFO result.py(73) - 开始测试: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,441   23748     INFO result.py(172) - 测试执行通过: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,442   23748     INFO result.py(96) - 测试结束: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,442   23748     INFO result.py(97) - 耗时: 4.770826101303101
2023-03-07 17:16:09,611   25872     INFO test_runner.py(199) - 
Pass	test_windows_size (test_baidu.TestCase)

2023-03-07 17:16:09,612   25872    ERROR test_runner.py(201) - 
Error	test_back_refresh (test_baidu.TestCase)
Error	test_search (test_baidu.TestCase)
Error	test_serach_clear (test_baidu.TestCase)

2023-03-07 17:16:09,614   25872     INFO test_runner.py(219) - 
测试结束!
运行时间: 0:00:13.857089
共计执行用例数量:4
执行成功用例数量:1
执行失败用例数量:0
跳过执行用例数量:0
产生异常用例数量:3

5.3.3 xxx.xml

<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="关于HTMLReport的实际项目应用" errors="3" failures="0" tests="4" skipped="0" time="13.857089" >
    <testsuite name="test_baidu.TestCase" id="0" errors="3" skipped="0" tests="4" failures="0" time="6.934138298034668">
        <testcase name="test_back_refresh" classname="test_baidu.TestCase.test_back_refresh" time="0.14696788787841797">
            <error/>
        </testcase>
        <testcase name="test_search" classname="test_baidu.TestCase.test_search" time="2.0103440284729004">
            <error/>
        </testcase>
        <testcase name="test_serach_clear" classname="test_baidu.TestCase.test_serach_clear" time="0.0060002803802490234">
            <error/>
        </testcase>
        <testcase name="test_windows_size" classname="test_baidu.TestCase.test_windows_size" time="4.770826101303101">
        </testcase>
    </testsuite>
</testsuites>

5.4 testcase

  • 注意:这个用例只是说明测试报告的生成,没有对用例严格按照标准写,比如断言等
# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:test_baidu.py
# Function:打开百度网主页,在搜索栏输入“helloworld”

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import unittest


class TestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.driver = webdriver.Chrome()  # 打开Chrome浏览器
        cls.driver.get("http://www.baidu.com")  # 输入百度网址
        print("============验证浏览器的基本控制==========")

    @classmethod
    def tearDownClass(cls) -> None:
        cls.driver.quit()  # 关闭浏览器

    def test_search(self):
        print("1、搜索helloworld.并回车......")
        time.sleep(2)
        self.driver.find_element_by_id("kw").send_keys("helloworld")  # 输入“helloworld”
        time.sleep(2)
        self.driver.find_element_by_id("kw").send_keys(Keys.ENTER)  # 回车进行搜索
        time.sleep(2)
        self.driver.maximize_window()  # 最大化当前窗口

    def test_windows_size(self):
        print("2、浏览器窗口大小缩小为640*480......")
        time.sleep(2)
        self.driver.set_window_size(640, 480)  # 控制浏览器显示尺寸为640*480
        time.sleep(0.5)
        self.driver.maximize_window()  # 最大化当前窗口
        time.sleep(2)

    def test_back_refresh(self):
        print("3、先进行浏览器后退,再次输入csdn进行搜索")
        self.driver.back()
        self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
        time.sleep(1)
        self.driver.refresh() # 刷新

    def test_serach_clear(self):
        print("4、清空输入的内容......")
        self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
        time.sleep(2)
        self.driver.find_element_by_id("kw").clear()
        time.sleep(0.5)

    def csdn(self):
        print("5、进入csdn官网")
        self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
        time.sleep(2)
        self.driver.find_element_by_id("kw").send_keys(Keys.ENTER)  # 回车进行搜索
        time.sleep(2)
        self.driver.find_element_by_xpath("//*[@id='1']/h3/a[1]").click()
        time.sleep(2)
        windows = self.driver.window_handles
        self.driver.switch_to.window(windows[-1])
        now_url = self.driver.current_url
        m_get_url = "https://www.csdn.net/"
        if now_url == m_get_url:
            print("经过判断,已经进入csdn官网!!")
        else:
            print("未进入到csdn官网,请检查代码!")


if __name__ == "__main__":
    unittest.main()



5.5 main.py

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:main.py
# 作用:框架的主入口函数

import time
from common.reportOut import report_out
from common.sendMain import send_main
import os


def acquire_report_address(reports_address):
    #这里方法略获取最新的测试报告,作为邮件的附件


def run_case():
    print("======开始执行!!!======")
    curpath = os.path.dirname(os.path.realpath(__file__))
    report_dir = os.path.join(curpath, "report/")        # 测试报告存放目录
    test_dir = os.path.join(curpath, "testcase/")        # 测试用例读取目录
    name_project = "关于HTMLReport的实际项目应用"
    report_out(test_dir, report_dir, name_project)
    time.sleep(5)
    # 这里方法略,调用邮件方法即可
    print("======执行结束!!!======")


if __name__ == '__main__':
    run_case()

6 运行结果

  • 会在report目录下生成三个文件;
    在这里插入图片描述

  • 命令行输出:
    在这里插入图片描述

  • 测试报告:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。