jjzjj

Python学习之UnitTest【使用,生成HTML测试报告】

阿杰的代码空间 2024-02-02 原文

目录

1、 UnitTest

1.1 TestCase

1.2 TestSuite

1.3 TextTestRunner

1.4 TestLoader

2、Fixture

2.1 方法级别

2.2 类级别

2.3 模块级别

2.4 总结

3、断言

3.1 UnitTest 常用断言方法

3.2 使用方式

4、参数化

4.1 安装 类库

4.2 使用

5、跳过

6、生成HTML测试报告


UnitTest 是 Python 自带的一个单元测试框架,用它来做单元测试。

为什么使用UnitTest框架?

  • 能够组织多个用例去执行;

  • 提供丰富的断言方法;

  • 能够生成测试报告;

1、 UnitTest

UnitTest 核心要素

  1. TestCase;

  2. TestSuite;

  3. TestRunner;

  4. TestLoader;

  5. Fixture;

1.1 TestCase

TestCase 就是测试用例的意思。

示例:

我们可以定义一个实现加法操作的函数,并对该函数进行测试。

# 导包
import unittest


# 定义函数(方法)
def my_sum(i, j):
    return i + j;


# 定义测试类 注:必须继承unittest.TestCase
class my_test(unittest.TestCase):
    # 定义测试方法 注: 测试方法名称命名以 test 开头;
    def test_01(self):
        print(my_sum(1, 2))

    def test_02(self):
        print(my_sum(3, 4))

1.2 TestSuite

TestSuite 翻译过来的意识就是 测试套件,多条测试用例集合在一起,就是一个 TestSuite。

使用:

1、实例化(suite:为 TestSuite 实例化的名称,你可以叫a,b,c 都可以)

suite = unittest.TestSuite()

2、添加用例(ClassName:为类名;MethodName:为方法名)

suite.addTest(ClassName("MethodName"))

3、添加扩展:(搜索指定 ClassName 内 test 开头的方法并添加到测试套件中)

suite.addTest(unittest.makeSuite(ClassName))

注:

TestSuite 需要配合 TextTestRunner 才能被执行

1.3 TextTestRunner

TextTestRunner 是用来执行测试用例和测试套件的

使用:

1、实例化: runner = unittest.TextTestRunner()

2、执行: runner.run(suite) # suite:为测试套件名称

示例:

这里我们多写将测试类分模块。

Test01.py

# 导包
import unittest


# 定义函数(方法)
def my_sum(i, j):
    return i + j;


# 定义测试类 my_test 注:必须继承unittest.TestCase
class my_test(unittest.TestCase):
    # 定义测试方法 注: 测试方法名称命名以 test 开头;
    def test_01(self):
        print("结果:%s" % my_sum(1, 2)+" my_test的test_01")

    def test_02(self):
        print("结果:%s" % my_sum(3, 4)+" my_test的test_02")

Test02.py

# 导包
import unittest


# 定义函数(方法)
def my_sum(i, j):
    return i + j;


class my_test02(unittest.TestCase):
    def test_01(self):
        print("结果:%s" % my_sum(1, 2)+" my_test02的test_01")

    def test_02(self):
        print("结果:%s" % my_sum(1, 2)+" my_test02的test_02")

Test.py :执行测试用例

import unittest
import Test01
import Test02

# 实例化TestSuite
suite = unittest.TestSuite()
# 添加用例
suite.addTest(Test01.my_test("test_01"))
# 添加 my_test02 类中所有 test 开头的方法
suite.addTest(unittest.makeSuite(Test02.my_test02))
# 实例化TextTestRunner
runner = unittest.TextTestRunner()
# 执行
runner.run(suite)

1.4 TestLoader

用来加载 TestCase 到 TestSuite 中,即加载满足条件的测试用例,并把测试用例封装成测试套件。

使用 unittest.TestLoader,通过该类下面的 discover()方法自动搜索指定目录下指定开头的.py 文件,并将查找到的测试用例组装到测试套件;

使用:

suite = unittest.TestLoader().discover(test_dir, pattern='test*.py')

test_dir: 为指定的测试用例的目录;

pattern:为查找的.py 文件的格式;

注:

如果文件名默认为'test*.py' 也可以使用 unittest.defaultTestLoader 代替 unittest.TestLoader()

示例:

import unittest

# 实例化 TestLoader
suite = unittest.TestLoader().discover("./", "test0*.py")
# 实例化TextTestRunner
runner = unittest.TextTestRunner()
# 执行
runner.run(suite)

TestSuite TestLoader 区别

  • TestSuite 需要手动添加测试用例(可以添加测试类,也可以添加测试类中某个测试方法);

  • TestLoader 搜索指定目录下指定开头.py 文件,并添加测试类中的所有的测试方法,不能指定添加测试方法;

2、Fixture

Fixture 是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture 。

Fixture有三个控制级别:

2.1 方法级别

在TestCase,也就是测试用例所在的class中定义方法,如果一个TestCase中有多个测试用例,那么setUp和tearDown就会被自动调用多次。

使用:

  • 初始化(前置处理): def setUp(self) --> 首先自动执行;

  • 销毁(后置处理): def tearDown(self) --> 最后自动执行;

运行于测试方法的始末,即:运行一次测试方法就会运行一次 setUp 和tearDown

示例:

# 导包
import unittest


# 定义函数(方法)
def my_sum(i, j):
    return i + j;


# 定义测试类 my_test 注:必须继承unittest.TestCase
class my_test(unittest.TestCase):
    # 初始化方法
    def setUp(self):
        print("setUP 执行初始化")

    # 销毁方法
    def tearDown(self):
        print("tearDown 执行销毁")

    # 定义测试方法 注: 测试方法名称命名以 test 开头;
    def test_01(self):
        print("结果:%s" % my_sum(1, 2) + " my_test的test_01")

    def test_02(self):
        print("结果:%s" % my_sum(3, 4) + " my_test的test_02")

2.2 类级别

不管类中有多少方法,一个类开始的时候自动调用函数,结束的之后自动调用函数。

使用:

  • 初始化(前置处理): @classmethod def setUpClass(cls): --> 首先自动执行

    • 销毁(后置处理): @classmethod def tearDownClass(cls): --> 最后自动执行

运行于测试类的始末 , 即: 每个测试类只会运行 一次 setUpClass 和 tearDownClass.

# 导包
import unittest


# 定义函数(方法)
def my_sum(i, j):
    return i + j;


# 定义测试类 my_test 注:必须继承unittest.TestCase
class my_test(unittest.TestCase):
    # 初始化
    @classmethod
    def setUpClass(cls):
        print("setUP 执行初始化")

    # 销毁方法
    @classmethod
    def tearDownClass(cls):
        print("tearDown 执行销毁")

    # 定义测试方法 注: 测试方法名称命名以 test 开头;
    def test_01(self):
        print("结果:%s" % my_sum(1, 2) + " my_test的test_01")

    def test_02(self):
        print("结果:%s" % my_sum(3, 4) + " my_test的test_02")

2.3 模块级别

不管py文件中有多少个类,以及类中有多少方法,只自动执行一次

使用:

  • 初始化(前置处理): def setUpModule(): --> 首先自动执行

  • 销毁(后置处理): def tearDownModule(): --> 最后自动执行

# 导包
import unittest


# 定义函数(方法)
def my_sum(i, j):
    return i + j;


# 模块级
def setUpModule():
    print("setUpModule自动调用了")


# 模块级
def tearDownModule():
    print("tearDownModule自动调用了")


# 定义测试类 my_test 注:必须继承unittest.TestCase
class my_test(unittest.TestCase):
    # 定义测试方法 注: 测试方法名称命名以 test 开头;
    def test_01(self):
        print("结果:%s" % my_sum(1, 2) + " my_test的test_01")

    def test_02(self):
        print("结果:%s" % my_sum(3, 4) + " my_test的test_02")


# 定义测试类 my_test02
class my_test02(unittest.TestCase):
    def test_01(self):
        print("结果:%s" % my_sum(1, 2) + " my_test02的test_01")

    def test_02(self):
        print("结果:%s" % my_sum(1, 2) + " my_test02的test_02")

2.4 总结

  • 必须继承 unittest.TestCase 类,setUp、tearDown 才是一个 Fixture;

  • 方法级:setUp,tearDown:如果一个类中有多个测试用例,每执行一个测试用例之前会调用一次 setUp,之后会调用一次 tearDown;

  • 类级:setUpClass,tearDownClass:如果一个类中有多个测试用例,执行所有测试用例之前只会调用一次 setUpClass,之后只会调用一次 tearDownClass;

  • 模块级:setUpModule,tearDownModule:只在 import 导入这个模块时会调用一次 setUpModule,模块使用完成之后会调用一次 tearDownModule;

  • setUpXXX:一般做初始化工作; tearDownXXX:一般做结束工作;

3、断言

让程序代替人为判断测试程序执行结果是否符合预期结果的过程。

为什么要学习断言呢?

因为自动化脚本在执行的时候一般都是无人值守状态,我们不知道执行结果是否符合预期结果,所以我们需要让程序代替人为检测程序执行的结果是否符合预期结果,这就需要使用断言。

3.1 UnitTest 常用断言方法

UnitTest 中提供了非常丰富的断言方法,复杂的断言方法在自动化测试中几乎使用不到,所以我们只需要掌握几个常用的即可。

常用的 UnitTest 断言方法:

序号断言方法断言描述
1assertTrue(expr, msg=None)验证 expr 是 true,如果为 false,则 fail
2assertFalse(expr, msg=None)验证 expr 是 false,如果为 true,则 fail
3assertEqual(expected, actual, msg=None)验证 expected==actual,不等则 fail
4assertNotEqual(first, second, msg=None)验证 first != second, 相等则 fail
5assertIsNone(obj, msg=None)验证 obj 是 None,不是则 fail
6assertIsNotNone(obj, msg=None)验证 obj 不是 None,是则 fail
7assertIn(member, container, msg=None)验证是否 member in container
8assertNotIn(member, container, msg=None)验证是否 member not in container

3.2 使用方式

断言方法已经在 unittest.TestCase 类中定义好了,而且我们自定义的测试类已经继承了 TestCase,所以在测试方法中直接调用即可。

# 导包
import unittest


# 定义函数(方法)
def my_sum(i, j):
    return i + j;


# 定义测试类 my_test 注:必须继承unittest.TestCase
class my_test(unittest.TestCase):
    # 定义测试方法 注: 测试方法名称命名以 test 开头;
    def test_01(self):
        num = my_sum(1, 2)
        # 如果 num为4,正确
        self.assertEqual(3, num);

    def test_02(self):
        num = my_sum(3, 4)
        # 如果 num为7,正确
        self.assertEqual(7, num);

    def test_03(self):
        num = my_sum(1, 2)
        # 如果 num在列表中,正确
        self.assertIn(num,[1,2,3,4,5])

4、参数化

上面的测试用例都存在一个问题,都是一条测试数据定义一个测试函数,代码冗余度太高。

我们可以通过参数化的方式来传递数据,从而实现数据和脚本分离。

并且可以实现用例的重复执行。unittest测试框架,本身不支持参数化,但是可以通过安装 unittest扩展插件 parameterized 来实现。

4.1 安装 类库

方式一:

我们可以直接打开命令提示符输入:pip install parameterized

方式二 :

使用PyCharm 安装,直接看图

 

 

4.2 使用

方式一:

import unittest
from parameterized import parameterized


def my_sum(i, j):
    return i + j


class my_test(unittest.TestCase):
    # a是调用my_sum的第一个参数
    # b是调用my_sum的第二个参数
    # c是预期结果
    @parameterized.expand([(1, 2, 3), (5, 6, 110), (-1, 3, 2)])
    def test_001(self, i, j, k):
        # 定义变量num得到my_sum函数的返回值
        num = my_sum(i, j)
        # num1里存放的是实际结果,k是预期结果
        self.assertEqual(num, k)
        # 实际结果与预期结果相符,代表测试用例测试通过
        # 不相符代表测试用例测试失败

方式二:

就是直接定义好一个列表,列表里面有元组。

方式三:

就是定义一个函数(方法),然后直接返回元组。

5、跳过

对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行。

使用方式:

@unittest.skip('代码未完成'):直接将测试函数标记成跳过

@unittest.skipIf(condition, reason):根据条件判断测试函数是否跳过

示例:

6、生成HTML测试报告

HTML 测试报告就是执行完测试用例后,以 HTML(网页)方式将执行结果生成报告。

为什么要生产测试报告?

因为测试报告是本次测试结果的体现形态,然后测试报告内包含了有关本次测试用例的详情;

HTML 生成报告方式

一种是TextTestRunner (UnitTest 自带),另外的就是其他的第三方模板HTMLTestRunner。

首先我们先来看看TextTestRunner如何生成测试报告的。

import unittest

# 生成测试套件
suite = unittest.TestLoader().discover("./", "test0*.py")
# 以只写方式打开测试报告文件
f = open("C:/Users/jie/Desktop/test.txt", "w", encoding="utf-8")
# 实例化TextTestRunner stream为open函数打开的文件流; verbosity 为不同模板编号
runner = unittest.TextTestRunner(stream=f,verbosity=2)
# 执行
runner.run(suite)
# 关闭
f.close()

结果

我们再来看看HTMLTestRunner 测试报告

首先要安装HTMLTestRunner,这里要注意的是由于HTMLTestRunner是一个第三方的unittest HTML报告库,用pip是死活安装不了的,得去网上下载HTMLTestRunner.py放到存放python源代码的Lib目录下。大家也可以用我下好的。

HTMLTestRunner: 这是python HTMLTestRunner

import unittest
from HTMLTestRunner import HTMLTestRunner

# 生成测试套件
suite = unittest.TestLoader().discover("./", "test*.py")
# 以只写方式打开测试报告文件
f = open("C:/Users/jie/Desktop/test01.html", "wb")
# 实例化 HTMLTestRunner 对象  stream:open 函数打开的文件流; title:[可选参数],为报告标题; description:[可选参数],为报告描述信息;比如操作系统、浏览器等版本;
runner = HTMLTestRunner(stream=f, title="自动化测试报告", description="Chrome 浏览器")
# 执行
runner.run(suite)
# 关闭
f.close()

 效果

有关Python学习之UnitTest【使用,生成HTML测试报告】的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐