7.unittest扩展

1. HTML测试报告

HTMLTestRunner 是 unittest 的一个扩展,它可以生成易于使用的 HTML 测试报告。可在Python3下运行的,地址:GitHub 地址:https://github.com/defnngj/HTMLTestRunner。

(1)下载与安装

打开上面的 GitHub 地址,克隆或下载整个项目。然后把 HTMLTestRunner.py 单独放到 Python 的安装目录下面,如 C:\Python37\Lib\。命令行窗口输入python,导入 HTMLTestRunner 验证安装是否成功。(如果提示找不到jinja2模块则通过“pip3 install jinja2”安装,若提示找不到__init__.py文件,则从下载的项目中找到和HTMLTestRunner.py文件放在一起的__init__.py文件也放到 Python 的安装目录下)
如果把 HTMLTestRunner 当作项目的一部分来使用,就把它放到项目目录中。(同时还需要把__init__.py 文件和 html文件夹放在项目目录中)例如:
unittest_expansion/
├─html/
│ └─template.html
│ └─…
├─test_case/
│ └─test_baidu.py
├─test_report/
├─__init__.py
├─HTMLTestRunner.py
└─run_tests.py

(2)生成 HTML 测试报告

查看第六篇文章中1.(4)中的 run_tests.py 文件,测试用例的执行是通过 TextTestRunner 类提供的 run()方法完成的。这里需要把 HTMLTestRunner.py 文件中的 HTMLTestRunner 类替换 TextTestRunner 类。
在这里插入图片描述
HTMLTestRunner 类 __init __() 初始化方法的参数如下:

  • stream:指定生成 HTML 测试报告的文件,必填。
  • verbosity:指定日志的级别,默认为 1。更详细的日志可以将参数修改为 2。
  • title:指定测试用例的标题,默认为 None。
  • description:指定测试用例的描述,默认为 None。

修改 run_tests.py 文件如下:

import unittest
from HTMLTestRunner import HTMLTestRunner

# 定义测试用例的目录为当前目录下的 test_cas 目录
test_dir = './test_case'
suit = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')

if __name__ == '__main__':
    # 生成 HTML 格式的报告
    fp = open('./test_report/result.html', 'wb')
    runner = HTMLTestRunner(stream=fp,
                            title='百度搜索测试报告',
                            description='运行环境: Windows 10, Chrome 浏览器')
    runner.run(suit)
    fp.close()

使用 open()方法打开 result.html 文件,用于写入测试结果,没有时自动创建。最后,关闭 result.html 文件。
打开/test_report/result.html 文件:
在这里插入图片描述

(3)更易读的测试报告

现在生成的测试报告仅显示测试类名和测试方法名。在编写功能测试用例时,每条测试用例都有标题或说明,自动化测试用例也能加上中文的标题或说明。
Python 的注释有两种,一种叫作 comment,另一种叫作 doc string。前者为普通注释,后者用于描述函数、类和方法。
doc string 类型的注释在平时调用时不会显示,只有通过 help()方法查看时才会被显示出来。因为 HTMLTestRunner 可以读取 doc string 类型的注释,所以,我们只需给测试类或方法添加这种类型的注释即可。

class TestBaidu(unittest.TestCase):
	""" 百度搜索测试 """
	……
	def test_search_key_selenium(self):
 		"""" 搜索关键字:selenium """
 		……
	def test_search_key_unttest(self):
 		"""" 搜索关键字:unittest """
 		……

在这里插入图片描述

(4)测试报告文件名

因为测试报告的名称是固定的,所以每次新的测试报告都会覆盖上一次的。
我们最好能为测试报告自动取不同的名称,并且还要有一定的含义。时间是个不错的选择,因为它可以标识每个报告的运行时间,更主要的是,时间永远不会重复。

>>> import time
>>> time.time()
1626682967.8470786
>>> time.ctime()
'Mon Jul 19 16:23:01 2021'
>>> time.localtime()
time.struct_time(tm_year=2021, tm_mon=7, tm_mday=19, tm_hour=16, tm_min=23, tm_sec=10, tm_wday=0, tm_yday=200, tm_isdst=0)
>>> time.strftime("%Y_%m_%d %H:%M:%S")
'2021_07_19 16:24:23'
>>>
  • time.time():获取当前时间戳。
  • time.ctime():当前时间的字符串形式。
  • time.localtime():当前时间的 struct_time 形式。
  • time.strftime():用来获取当前时间,可以将时间格式化为字符串。

修改 run_tests.py,修改部分如下:

if __name__ == '__main__':
    # 取当前日期时间
    now_time = time.strftime("%Y-%m-%d %H_%M_%S")
    # 生成 HTML 格式的报告
    fp = open('./test_report/' + now_time + 'result.html', 'wb')

通过 strftime()方法以指定的格式获取当前日期时间,并赋值给 now_time 变量。将 now_time 通过加号(+)拼接到生成的测试报告的文件名中。
在这里插入图片描述

2. 数据驱动应用

数据驱动是自动化测试的一个重要功能,抛开单元测试框架谈数据驱动的使用是没有意义的。

(1)数据驱动

数据的改变(更新)驱动自动化的执行,从而引起测试结果的改变。我们可以直接理解成参数化,输入数据的不同从而引起输出结果的变化。
在 unittest 中,可以使用读取数据文件来实现参数化。
创建 baidu_data.csv 文件,如下:
在这里插入图片描述
创建 test_baidu_data.py 文件,代码如下:

import csv
import codecs
import unittest
from time import sleep
from itertools import islice
from selenium import webdriver

class TestBaidu(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = "https://www.baidu.com"

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id('kw').send_keys(search_key)
        self.driver.find_element_by_id('su').click()
        sleep(3)

    def test_search(self):
        with codecs.open("baidu_data.csv", "r", "utf_8_sig") as f:
            data = csv.reader(f)
            for line in islice(data, 1, None):
                search_key = line[1]
                self.baidu_search(search_key)


if __name__ == '__main__':
    unittest.main(verbosity=2)

在这里插入图片描述
这里所有的测试数据被当做一条测试用例执行,这样划分不合理,3条数据对应3条测试用例更为合适,前面执行失败的测试用例不影响后面的。
修改 test_baidu_data.py 文件,修改如下:

import csv
import codecs
import unittest
from time import sleep
from itertools import islice
from selenium import webdriver


class TestBaidu(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = "https://www.baidu.com"
        cls.test_data = []
        with codecs.open("baidu_data.csv", "r", "utf_8_sig") as f:
            data = csv.reader(f)
            for line in islice(data, 1, None):
                cls.test_data.append(line)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id('kw').send_keys(search_key)
        self.driver.find_element_by_id('su').click()
        sleep(3)

    def test_search_selenium(self):
        self.baidu_search(self.test_data[0][1])

    def test_search_unittest(self):
        self.baidu_search(self.test_data[1][1])

    def test_search_parameterized(self):
        self.baidu_search(self.test_data[2][1])


if __name__ == '__main__':
    unittest.main(verbosity=2)

在这里插入图片描述
从结果中看,3 条数据被当作 3 条测试用例执行了。

读取数据文件也带来了两点问题:

  • 增加了读取的成本。任何数据文件都需要将文件中的数据读取到程序中。
  • 不方便维护。
    在 CSV 数据文件中,并不能直观体现出每一条数据对应的测试用例。上面获取数据也存在问题,如果在 CSV 文件中间插入了一条数据,那么测试用例获取到的测试数据很可能就是错的。
    如果测试过程中需要用很多数据呢?那么我们需要知道,UI 自动化测试是站在用户的角度模拟用户的操作,输入大量数据的功能很少,若真的需要输入大量数据,那么可能用户体验做得不好。

读取数据文件并非完全没必要,比如一些自动化测试的配置就可以放到数据文件中,如运行环境、运行的浏览器等,放到配置文件中会更方便管理。

(2)Parameterized

Parameterized 是 Python 的一个参数化库,同时支持 unittest、Nose 和 pytest 单元测试框架。
Parameterized 支持 pip 安装。 pip install parameterized
用参数化库来实现参数化,创建 test_baidu_parameterized.py 文件,代码如下:

import unittest
from time import sleep
from selenium import webdriver
from parameterized import parameterized


class TestBaidu(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = "https://www.baidu.com"

    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id('kw').send_keys(search_key)
        self.driver.find_element_by_id('su').click()
        sleep(2)

    # 通过 Parameterized 实现参数化
    @parameterized.expand([
        ('case1', 'selenium'),
        ('case2', 'unittest'),
        ('case3', 'parameterized'),
    ])
    def test_search(self, name, search_key):
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == '__main__':
    unittest.main(verbosity=2)

在这里插入图片描述
代码中导入parameterized 类,然后通过 @parameterized.expand() 来装饰测试用例 test_search()。
在@parameterized. expand()中,每个元组都可以被认为是一条测试用例。元组中的数据为该条测试用例变化的值。在测试用例中,通过参数(这里是 name 和 search_key)来取每个元组中的数据。
unittest 的 main()方法设置 verbosity 参数为 2,输出更详细的执行日志。

测试结果根据@parameterized.expand()中元组的个数来统计测试用例数的,参数化会自动加上“0”、“1”和“2”来区分每条测试用例,在元组中定义的 name 也会作为每条测试用例名称的后缀出现。

(3)DDT

DDT(Data-Driven Tests)是针对 unittest 单元测试框架设计的扩展库。允许使用不同的测试数据来运行一个测试用例,并将其展示为多个测试用例。DDT 支持 pip 安装。 pip install ddt

创建 test_baidu_ddt.py 文件,代码如下:

import unittest
from time import sleep
from selenium import webdriver
from ddt import ddt, data, file_data, unpack

@ddt
class TestBaidu(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.base_url = "http://www.baidu.com"

    def baidu_search(self, search_key):
        self.driver.get(self.base_url)
        self.driver.find_element_by_id('kw').send_keys(search_key)
        self.driver.find_element_by_id('su').click()
        sleep(2)

    # 参数化使用方式一
    @data(['case1', 'selenium'], ['case2', 'ddt'], ['case3', 'python'])
    @unpack  # @unpack,['case1', 'selenium']被分解开,按照用例中的两个参数传递
    def test_search1(self, case, search_key):
        print("第一组测试用例:", case)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    # 参数化使用方式二
    @data(('case1', 'selenium'), ('case2', 'ddt'), ('case3', 'python'))
    @unpack
    def test_search2(self, case, search_key):
        print("第二组测试用例:", case)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    # 参数化使用方式三
    @data({'search_key': 'selenium'}, {'search_key': 'ddt'}, {'search_key': 'python'})
    @unpack
    def test_search3(self, search_key):
        print("第三组测试用例:", search_key)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == '__main__':
    unittest.main(verbosity=2)

在这里插入图片描述
使用 DDT 时,测试类需要通过@ddt 装饰器进行装饰。DDT 提供了不同形式的参数化,上述代码又三组参数化:列表,元组,字典。需要注意的是,字典的 key 与测试方法的参数要保持一致。

DDT 同样支持数据文件的参数化。它封装了数据文件的读取,让我们更专注于数据文件中的内容,以及在测试用例中的使用,无须关心数据文件如何被读取。

这里举例DDT支持的 JSON,YAML 格式数据文件。读取 YAML 格式文件 需要安装 PyYaml 模块(pip install pyyaml)。
分别创建 ddt_data_file.json 文件 和 ddt_data_file.yaml 文件,如下:(yaml 文件中的加 “-” 缩进表示 list 格式)
在这里插入图片描述
在这里插入图片描述
修改 test_baidu_ddt.py 文件,添加代码如下:

    # 参数化读取 JSON 文件
    @file_data('../data_file/ddt_data_file.json')
    def test_search4(self, search_key):
        print("第四组测试用例:", search_key)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    # 参数化读取 yaml 文件
    @file_data('../data_file/ddt_data_file.yaml')
    def test_search5(self, case):
        search_key = case[0]['search_key']
        print("第五组测试用例:", search_key)
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索")

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

3. 自动发送邮件功能

自动发送邮件功能是自动化测试项目的重要需求之一,当自动化测试用例运行完成之后,可自动向相关人员的邮箱发送测试报告。发送邮件模块并不属于 unittest 的扩展,可以将它与 unittest 结合使用。

SMTP(Simple Mail Transfer Protocol)是简单邮件传输协议,是一组由源地址到目的地址传送邮件的规则,可以控制信件的中转方式。Python 的 smtplib 模块提供了简单的 API用来实现发送邮件功能,它对 SMTP 进行了简单的封装。

发邮件基础知识:有一个自己的邮箱,通过浏览器打开邮箱网址,或打开邮箱客户端登录自己的邮箱账号,若是邮箱客户端,则需要配置邮箱服务器地址。然后填写收件人地址、邮件的主题和正文,以及添加附件等。

(1)Python 自带的发送邮件功能

在发送邮件时,除填写主题和正文外,还可以增加抄送人、添加附件等,这里邮件正文和附近都是测试报告。

a. 发送邮件正文

import smtplib
from email.mime.text import MIMEText
from email.header import Header

# 发送邮件主题
subject = 'Python email test'

# 编写 HTML 类型的邮件正文
msg = MIMEText('<html><h1>你好!</h1></html>', 'html', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')

# 发送邮件
smtp = smtplib.SMTP()
smtp.connect('smtp.qq.com')
smtp.login('sender@qq.com', '*********') # 第二个参数为开启 POP3/SMTP 服务的授权码
smtp.sendmail('sender@qq.com', 'receiver@qq.com', msg.as_string())
smtp.quit()

登录收件人邮箱,查看邮件内容。
在这里插入图片描述

MIMEText 类,定义发送邮件的正文、格式,以及编码。
Header 类,定义邮件的主题和编码类型。
smtplib 模块用于发送邮件。connect()方法指定连接的邮箱服务;login()方法指定登录邮箱的账号和密码(此处是授权码);sendmail()方法指定发件人、收件人,以及邮件的正文; quit()方法用 于关闭邮件服务器的连接。

b. 发送带附件的邮件

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# 邮件主题
subject = 'Python send email test'
# 发送的附件
with open('../data_file/attachment.txt', 'rb') as f:
    send_att = f.read()

att = MIMEText(send_att, 'text', 'utf-8')
att['Content-Type'] = 'application/octet-stream'  # 指定附件内容类型:二进制流
att['Content-Disposition'] = 'attachment; filename="attachment.txt"'  # 指定显示附件的文件

msg = MIMEMultipart()
msg['Subject'] = subject
msg.attach(att)

# 发送邮件
smtp = smtplib.SMTP()
smtp.connect("smtp.qq.com")
smtp.login('sender@qq.com', '*********') # 第二个参数为开启 POP3/SMTP 服务的授权码
smtp.sendmail('sender@qq.com', 'receiver@qq.com', msg.as_string())
smtp.quit()

在这里插入图片描述
MIMEText 类,定义发送邮件的正文、格式,以及编码;Content-Type 指定附件内容类型;application/octet-stream 表示二进制流;Content-Disposition指定显示附件的文件;attachment; filename="attachment.txt"指定附件的文件名。
使用 MIMEMultipart 类定义邮件的主题,attach()指定附件信息。

(2)用 yagmail 发送邮件

yagmail 是 Python 的一个第三方库,可以让我们以非常简单的方法实现自动发送邮件功能。通过 pip 安装:pip install yagmail。yagmail 库极大地简化了发送邮件的代码。

import yagmail

# 连接邮箱服务器
yag = yagmail.SMTP(user='sender@qq.com', password='**********',
                   host='smtp.qq.com')
# 邮件正文
contents = ['This is the text, attached you can see a picture and a TXT file']
# 发送邮件
yag.send('receiver@qq.com', 'subject', contents)

在这里插入图片描述
代码中 password 为授权码。
若要给多个用户发送邮件,把收件人放到一个 list 中即可。
若要发送附件,给出文件路径参数即可,通过 list 可指定多个附件。

yag.send(['aa@qq.com', 'bb@qq.com', 'cc@qq.com'],
         'subject', contents,
         ['../data_file/image.png', '../data_file/attachment.txt'])

(3)整合自动发送邮件

将发送邮件功能集成到自动化测试项目中,修改run_tests.py 文件,代码如下:

import time
import unittest
import yagmail
from HTMLTestRunner import HTMLTestRunner


# 把测试报告作为附件发送到指定邮箱
def send_email(report):
    yag = yagmail.SMTP(user='1033756379@qq.com',
                       password='hspkyaqhbmpwbcea',
                       host='smtp.qq.com')
    subject = "主题,自动化测试报告"
    contents = "正文,请查看附件"
    yag.send('2207087346@qq.com', subject, contents, report)
    print("email has send out")


if __name__ == '__main__':
    # 定义测试用例的目录为当前目录
    test_dir = './test_case'
    suit = unittest.defaultTestLoader.discover(test_dir, pattern='test*。py')

    now_time = time.strftime("%Y-%m-%d %H_%M_%S")
    html_report = './test_report' + now_time + 'result.html'
    fp = open(html_report, 'wb')
    # 调用 HTMLTestRunner,运行测试用例
    runner = HTMLTestRunner(stream=fp,
                            title="百度搜索测试报告",
                            description="运行环境:Windows 10,Chrome 浏览器")
    runner.run(suit)
    fp.close()
    send_email(html_report)  # 发送报告

在这里插入图片描述
测试报告的内容不宜作为正文发送,因为 HTMLTestRunner 报告在展示时引用了 Bootstrap 样式库,当作为邮件正文“写死”在邮件中时,会导致样式丢失,所以作为附件发送更为合适。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: unittest.defaulttestloader.discover是Python中unittest模块中的一个函数,用于自动发现并加载指定目录下的测试用例。它会递归地查找目录下的所有测试文件,并将其中的测试用例加载到测试套件中。这个函数可以大大简化测试用例的管理和执行。 ### 回答2: unittest.defaultTestLoader.discover()是Python标准库unittest中的一个方法,用于自动发现指定目录下的测试用例并返回一个测试套件。通过该方法,可以方便地批量执行测试用例。 使用unittest.defaultTestLoader.discover()方法需要指定一个目录路径作为参数,该方法会递归地查找指定目录及其子目录下的所有测试用例文件。测试用例文件必须以'test'开头并以'.py'结尾,例如'test_*.py'。 使用该方法,可以自动加载并执行测试用例,无需手动编写测试套件。方法会自动查找测试用例文件并使用unittest.TestLoader()来加载测试用例。加载完成后,返回一个测试套件对象,其中包含了所有发现的测试用例。 返回的测试套件对象可以通过unittest.TextTestRunner().run()方法来执行测试。执行时,会依次运行测试用例,并生成测试结果报告。测试结果包括测试通过的用例数、失败的用例数、错误的用例数等信息。 使用unittest.defaultTestLoader.discover()方法,可以方便地批量执行测试用例,减少手动编写测试套件的工作量。同时,还能自动发现新增的测试用例文件,无需手动维护测试套件,提高了测试用例的可维护性。 总结来说,unittest.defaultTestLoader.discover()方法是一个用于自动发现测试用例并返回测试套件对象的方法,可以方便地批量执行测试用例,提高测试用例的可维护性。 ### 回答3: unittest.defaultTestLoader.discover 是 Python 中的一个单元测试模块,用于自动发现测试用例。在进行单元测试时,我们通常需要编写多个测试用例,然后运行这些用例来验证代码的正确性。然而,手动编写和管理所有的测试用例是一项繁琐的工作。这就是使用 discover 方法的好处所在。 discover 方法可以自动递归地从指定的目录中查找测试用例,包括子目录。它会根据一定的规则来确定哪些文件属于测试用例,然后自动加载这些测试用例。这样,我们只需要将测试用例按照一定的目录结构组织好,然后使用 discover 方法一次性加载所有的测试用例即可。 discover 方法可以接收以下几个参数:start_dir、pattern、top_level_dir。 start_dir 是要查找的目录,pattern 是要匹配的文件模式,可以使用通配符 * 、 ? 之的符号,top_level_dir 是顶层目录,用于计算测试用例的相对路径。 使用 discover 方法的步骤如下: 1. 创建一个测试包,将所有的测试用例文件放在这个包下面。 2. 在测试包的 __init__.py 文件中导入 unittest 模块,并定义一个 TestSuite 对象。 3. 使用 discover 方法查找测试用例,并将查找到的用例添加到 TestSuite 中。 4. 运行 TestSuite 来执行所有的测试用例。 通过使用 unittest.defaultTestLoader.discover,我们可以轻松地管理和执行大量的测试用例,提高了代码测试的效率和准确性。同时,使用 discover 方法还可以使测试用例的组织结构更加清晰,并且方便扩展和维护。总之,unittest.defaultTestLoader.discover 是一个非常好用的测试工具,可以极大地简化单元测试的流程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值