unittest是Python标准库中用于编写单元测试的模块。它提供了一系列用于编写和运行测试的工具和API,包括测试用例、测试套件、测试运行器和断言方法等。

unittest中的基本组件包括:

  1. TestCase:测试用例是一个包含一个或多个测试方法的类。测试方法应该以test_为前缀,以便unittest能够自动发现并运行它们。

  2. TestSuite:测试套件是一组测试用例的集合。它可以包含其他测试套件,这样可以方便地组织和运行一组相关的测试。

  3. TestRunner:测试运行器是用于运行测试套件的对象。unittest中提供了TextTestRunner、HTMLTestRunner等多种测试运行器。

  4. Assertions:断言方法是用于验证测试结果的方法,例如assertEqual()、assertTrue()、assertFalse()、assertRaises()等。

使用unittest编写单元测试通常需要继承unittest.TestCase类,并实现测试方法。在测试方法中,使用断言方法验证测试结果是否符合预期。然后将测试用例添加到测试套件中,最后通过测试运行器运行测试套件并查看测试结果。

unittest是Python中常用的单元测试框架之一,它提供了丰富的工具和API来支持单元测试的编写和运行。

第一个Unittest程序

编写测试用例前,我们需要建一个测试类继承unittest里面的TestCase类,继承这个类之后我们才是真正的使用unittest框架去写测试用例,编写测试用例的步骤如下:

  • 导入unittest模块

  • 创建一个测试类,并继承unittest.TestCase()

  • 定义测试方法,方法名必须以test_开头

  • 调用unittest.main()方法来运行测试用例,unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行

以下是一个简单的unittest示例程序:

import unittest

# 要测试的函数
def add(x, y):
    return x + y

# 继承unittest.TestCase类来编写测试用例
class TestAdd(unittest.TestCase):
    def test_add_positive_numbers(self):
        # 断言函数的返回值是否等于预期值
        self.assertEqual(add(2, 3), 5)

    def test_add_negative_numbers(self):
        self.assertEqual(add(-2, -3), -5)

    def test_add_zero(self):
        self.assertEqual(add(0, 0), 0)

# 运行测试用例
if __name__ == '__main__':
    unittest.main()

setUp

setUp 是 unittest中的一个特殊函数,在测试函数运行之前被调用。它可以用于为每个测试准备测试环境,例如创建测试数据或初始化系统设置。

unittest的setup分为两种,第一种是函数级别的setup,另外一种是类级别的setup。重写setUP方法即可使用setUp这个特殊函数

import unittest


class Test002(unittest.TestCase):

    def setUp(self):
        print("开始")

    @classmethod
    def setUpClass(cls):
        print("类开始")

    def test_format(self):
        str = "{0},{1}".format("hello", "world")
        print(str)

    def test_format_str(self):
        print("hello world")
        
# 运行测试用例
if __name__ == '__main__':
    unittest.main()

运行之后,test_formattest_format_str测试函数会在执行之前都会执行一次setUp里面的内容,而setUpClass会在测试类运行之前运行一次里面的内容。

注意setUpClass一定要加@classmethod装饰器,否则会报错。

tearDown

tearDown 是unittest中的一个特殊函数,在测试函数运行之后被调用。它可以用于清理测试环境,例如删除测试数据或重置系统设置。

unittest的tearDown分为两种,第一种是函数的tearDown,另外一种是类的tearDown。重写tearDown方法即可使用tearDown这个特殊函数。

import unittest


class Test002(unittest.TestCase):

    def tearDown(self):
        print("结束")

    @classmethod
    def tearDownClass(cls):
        print("类结束")

    def test_format(self):
        str = "{0},{1}".format("hello", "world")
        print(str)

    def test_format_str(self):
        print("hello world")

# 运行测试用例
if __name__ == '__main__':
    unittest.main()

运行之后,test_formattest_format_str测试函数会在执行之后都会执行一次tearDown里面的内容,而tearDownClass会在测试类运行之后运行一次里面的内容。

注意tearDownClass一定要加@classmethod装饰器,否则会报错。

TestSuite

在Python的unittest模块中,TestSuite是一个用于组织和运行一组测试用例的类。它可以包含多个TestCase对象或其他TestSuite对象,形成一个层次结构,从而方便地组织和运行测试。

例如,可以创建一个TestSuite对象,将多个相关的TestCase对象添加到其中,然后运行整个测试套件,以便同时运行所有测试。通过将测试用例组织在测试套件中,可以更好地管理和维护测试代码,并且可以使用不同级别的测试套件来运行不同范围的测试,从而更好地控制测试的细粒度程度。

举例:

import unittest

class Test001(unittest.TestCase):

    def test_A(self):
        print("Test A")

class Test002(unittest.TestCase):

    def test_B(self):
        print("Test B")

class Test003(unittest.TestCase):

    def test_C(self):
        print("Test C")

if __name__ == '__main__':
    test_suite = unittest.TestSuite()
    test_suite.addTest(Test001("test_A"))
    test_suite.addTest(Test002("test_B"))
    test_suite.addTest(Test003("test_C"))

    runner = unittest.TextTestRunner()
    runner.run(test_suite)

使用TestSuite的流程如下:

  • 新建TestSuite对象,unittest.TestSuite()

  • 调用addTest方法,新建测试对象,并填写测试方法。test_suite.addTest(Test001("test_A"))

  • 新建TextTestRunner对象,unittest.TextTestRunner()

  • 将装好用例的TestSuite对象放在Runner中运行,runner.run(test_suite)

addTest适合加载一个一个用例对象,上面例子中有三个用例对象,可以使用addTests方法一次加载多个用例对象

if __name__ == '__main__':
    test_suite = unittest.TestSuite()
    tests = [Test001("test_A"),Test002("test_B"),Test003("test_C")]
    test_suite.addTests(tests)

    runner = unittest.TextTestRunner()
    runner.run(test_suite)

若想一次加载完测试用例中所有的测试方法,可以用unittest.TestLoader().loadTestsFromTestCase(testCaseClass=testCaseClass)装载测试方法,例如:

import unittest

class Test001(unittest.TestCase):

    def test_A(self):
        print("Test A")

    def test_A2(self):
        print("Test A2")

    def test_A3(self):
        print("Test A3")

class Test002(unittest.TestCase):

    def test_B(self):
        print("Test B")

class Test003(unittest.TestCase):

    def test_C(self):
        print("Test C")

if __name__ == '__main__':
    test_suite = unittest.TestSuite()
    tests = [unittest.TestLoader().loadTestsFromTestCase(testCaseClass=Test001),Test002("test_B"),Test003("test_C")]
    test_suite.addTests(tests)

    runner = unittest.TextTestRunner()
    runner.run(test_suite)

TestRunner

TestRunner是Python中unittest模块中的一个类,用于执行测试用例并生成测试报告。unittest模块提供了多种TestRunner的实现,包括TextTestRunner、HTMLTestRunner等。其中,TextTestRunner是最常用的一种TestRunner,它会在控制台中输出测试结果的摘要信息。而HTMLTestRunner则会生成一个HTML格式的测试报告,用于展示测试结果、测试覆盖率等信息。

TestRunner中最重要的方法是run()方法,它会遍历测试套件中所有的测试用例,并执行每个测试用例中的测试方法。run()方法返回一个TestResult对象,它包含了测试结果的详细信息,包括测试用例的执行情况、测试用例中每个测试方法的执行情况、测试用例的通过率等。通常,我们可以将TestResult对象传递给TestRunner的构造函数,以便在测试完成后获取测试结果。

import unittest

class Test001(unittest.TestCase):

    def test_A(self):
        print("Test A")

if __name__ == '__main__':
    test_suite = unittest.TestSuite()
    test_suite.addTest(Test001("test_A"))

    runner = unittest.TextTestRunner()
    runner.run(test_suite)

DDT

"DDT"一词通常指的是"Data-Driven Testing"(数据驱动测试),它可以使用外部数据源来定义和执行测试用例,而不是硬编码测试用例。

安装

pip install ddt

ddt的四大模块

ddt与unittest一起结合使用,其库有四个常用模块,如下:

  1. @ddt: 引入ddt模块

  2. @data: 导入数据

  3. @unpack: 拆分数据

  4. @file_data: 导入文件数据

@ddt

为了让unittest导入ddt模块,需要在测试类前加@ddt以标明它为一个数据驱动型测试类

import unittest
from ddt import ddt, data


@ddt
class TestCases(unittest.TestCase):

    @data(1, 2, "name")
    def test_case_001(self, value):
        print(f"test_case_01 {value}")

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

运行结果:

============================= test session starts =============================
collecting ... collected 3 items

TestCases.py::TestCases::test_case_001_1_1 
TestCases.py::TestCases::test_case_001_2_2 
TestCases.py::TestCases::test_case_001_3_name 

============================== 3 passed in 0.52s ==============================

Process finished with exit code 0
PASSED                        [ 33%]test_case_01 1
PASSED                        [ 66%]test_case_01 2
PASSED                     [100%]test_case_01 name

@data

@data装饰器用于指定测试数据。它可以接受不同的参数形式来定义测试数据。在测试方法前添加@data装饰器即可使用。

@data可以传入多个数据,传入的参数可以是列表、元组、字典,unittest会根据这些数据执行对应的用例,其传入的语法如下:

@data(value01, value02, value03, ...)

直接传入数据

对测试方法中的单个形参传入多个数据进行测试,可以参考上面的例子。对测试方法中的多个形参传入多个数据进行测试,例子如下:

import unittest
from ddt import ddt, data, unpack


@ddt
class TestCases(unittest.TestCase):

    @data([1, 2, "name"], [3, 4, "age"], [5, 6, "company"])
    @unpack
    def test_case_001(self, value01, value02, value03):
        print(f"test_case_01 {value01} {value02} {value03}")


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

运行结果:

============================= test session starts =============================
collecting ... collected 3 items

TestCases.py::TestCases::test_case_001_1__1__2___name__ 
TestCases.py::TestCases::test_case_001_2__3__4___age__ 
TestCases.py::TestCases::test_case_001_3__5__6___company__ 

============================== 3 passed in 0.23s ==============================

Process finished with exit code 0
PASSED           [ 33%]test_case_01 1 2 name
PASSED            [ 66%]test_case_01 3 4 age
PASSED        [100%]test_case_01 5 6 company

可以看到,测试方法有多个形参的时候,在每个数据用列表将数据装好,并用@unpack对数据进行解包。如果不用@unpack,则将列表当作一个数据传入到测试方法里面。

如果重写tearDownsetUp@data每一次执行都会触发这些类运行,如:

import unittest
from ddt import ddt, data


@ddt
class TestCases(unittest.TestCase):

    def tearDown(self):
        print("在测试方法运行之后执行")

    def setUp(self):
        print("在测试方法运行之前执行")

    @data(1, 2, "name")
    def test_case_001(self, value01):
        print(f"test_case_01 {value01}")


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

运行结果:

============================= test session starts =============================
collecting ... collected 3 items

TestCases.py::TestCases::test_case_001_1_1 
TestCases.py::TestCases::test_case_001_2_2 
TestCases.py::TestCases::test_case_001_3_name 

============================== 3 passed in 0.16s ==============================

Process finished with exit code 0
PASSED                        [ 33%]在测试方法运行之前执行
test_case_01 1
在测试方法运行之后执行
PASSED                        [ 66%]在测试方法运行之前执行
test_case_01 2
在测试方法运行之后执行
PASSED                     [100%]在测试方法运行之前执行
test_case_01 name
在测试方法运行之后执行

调用函数

@data不仅仅可以直接传入数据,也可以通过函数将数据传入到@data中。比如:

import unittest
from ddt import ddt, data

def create_list(count):
    return count

@ddt
class TestCases(unittest.TestCase):

    @data(create_list(3))
    def test_case_001(self, value01):
        print(f"test_case_01 {value01}")

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

如果标明为多个数据,则函数可以返回一个列表,然后在@data中加*将列表解包,可以理解为将最外层的[]去掉。

import unittest
from ddt import ddt, data


def create_list(count):
    return [i for i in range(count)]

@ddt
class TestCases(unittest.TestCase):

    @data(*create_list(3))
    def test_case_001(self, value01):
        print(f"test_case_01 {value01}")


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

运行结果为:

============================= test session starts =============================
collecting ... collected 3 items

TestCases.py::TestCases::test_case_001_1_0 
TestCases.py::TestCases::test_case_001_2_1 
TestCases.py::TestCases::test_case_001_3_2 

============================== 3 passed in 0.17s ==============================

Process finished with exit code 0
PASSED                        [ 33%]test_case_01 0
PASSED                        [ 66%]test_case_01 1
PASSED                        [100%]test_case_01 2

对于多个形参,写法如下:

import unittest
import random
from ddt import ddt, data, unpack


def create_list(count):
    return [random.randint(0, 100) for i in range(count)]

@ddt
class TestCases(unittest.TestCase):

    @data(create_list(3), create_list(3), create_list(3))
    @unpack
    def test_case_001(self, value01, value02, value03):
        print(f"test_case_01 {value01} {value02} {value03}")


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

运行结果如下:

============================= test session starts =============================
collecting ... collected 3 items

TestCases.py::TestCases::test_case_001_1__11__99__63_ 
TestCases.py::TestCases::test_case_001_2__24__42__5_ 
TestCases.py::TestCases::test_case_001_3__40__65__98_ 

============================== 3 passed in 0.15s ==============================

Process finished with exit code 0
PASSED             [ 33%]test_case_01 11 99 63
PASSED              [ 66%]test_case_01 24 42 5
PASSED             [100%]test_case_01 40 65 98

上面也可以这么写:

import unittest
import random
from ddt import ddt, data, unpack


def create_list(count):
    list = []
    for i in range(count):
        list.append([random.randint(0, 100) for j in range(count)])
    return list

@ddt
class TestCases(unittest.TestCase):

    @data(*create_list(3))
    @unpack
    def test_case_001(self, value01, value02, value03):
        print(f"test_case_01 {value01} {value02} {value03}")


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

BeautifulReport

beautifulreport是Python中生成HTML格式的测试报告的第三方库,beautifulreport 提供了更加灵活的测试报告生成方式,支持自定义模板、主题等功能。

安装

进入github中把源码下载下来,直接放在项目使用即可

下载地址: https://github.com/TesterlifeRaymond/BeautifulReport

案例

import unittest
from beautifulreport import BeautifulReport

class TestMath(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(1+1, 2)
        
    def test_subtraction(self):
        self.assertEqual(3-1, 2)
        
    def test_multiplication(self):
        self.assertEqual(2*3, 6)

if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestMath))
    
    report_path = 'test_report.html'
    result = BeautifulReport(suite)
    result.report(filename=report_path, description='Test Report', log_path='.')

在这个例子中,我们导入了unittest和beautifulreport两个模块,并创建了一个测试用例类TestMath。然后,我们创建了一个测试套件suite并将TestMath类中的所有测试方法都添加到了测试套件中。接着,我们指定了测试报告的输出路径report_path。最后,我们使用BeautifulReport类创建了一个result对象,并使用report()方法生成测试报告,其中filename参数指定了输出文件名,description参数指定了测试报告的标题,log_path参数指定了日志文件的输出路径。

执行以上代码后,会在当前目录下生成一个名为test_report.html的文件,用浏览器打开该文件即可查看测试报告。测试报告中包含了测试结果的摘要、测试用例的执行情况、测试用例中每个测试方法的执行情况等详细信息,还可以查看测试覆盖率、测试用时等指标。

文章作者: Vsoapmac
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 soap的会员制餐厅
自动化测试 第三方库 个人分享 框架
喜欢就支持一下吧