Python:Unittest框架快速入门:用例、断言、夹具、套件、HTML报告、ddt数据驱动

快速看了套Unittest的入门教程

软件测试全套资料赠送_哔哩哔哩_bilibili软件测试全套资料赠送是快速入门unittest测试框架!全实战详细教学,仅此一套!的第1集视频,该合集共计11集,视频收藏或关注UP主,及时了解更多相关视频内容。icon-default.png?t=N7T8https://www.bilibili.com/video/BV19M4y147i6?p=1&vd_source=924f5dad6f2dcb0a3e5dca4604287ecd

整理一下学习时候整理的内容

目录

一、用例编写规则

二、模拟静态的登录数据

三、编写用例

3.1 导入unittest模块

3.2 创建测试类,该类继承unittest.TestCase

3.3 定义测试函数,函数名以test_开头,一个函数表示一个用例

3.4 编写用例

扫描二维码关注公众号,回复: 17265342 查看本文章

 3.5 普通的比对表达(if)

四、断言assert

4.1 断言方法

五、Fixture 夹具

5.1 测试用例的夹具

5.2 测试类的夹具

5.3 前置工作的数据传递给测试用例

5.3.1 setUp() 实例属性

5.3.2 setUpClass() 类属性

六、收集用例

6.1 TestSuite套件

6.1.1 addTest()方法

6.1.2 addTests()方法

6.2 TestLoader 加载用例

6.2.1 目录 discover

6.2.2 从测试类中加载

6.2.3 从模块名中加载

七、unittestreport - 生成HTML报告

7.1 初始化参数

八、ddt数据驱动

8.1 list_data

8.2 json_data

8.3 yaml


一、用例编写规则

  • 1、导入 unittest模块,被测文件或者其中的类
  • 2、创建一个测试类,并继承unittest.TestCase
  • 3、定义测试函数,函数名以 test_开头(表示一个测试用例)
  • 4、测试用例中:
    • 4.1 测试数据、期望结果
    • 4.2 用例步骤
    • 4.3 断言

二、模拟静态的登录数据

创建一个py文件:login_func.py

封装一个函数,用来进行账号密码登录的场景模拟

# 登录功能场景模拟
def login_check(userName=None, passWord=None):
    if userName is not None and passWord is not None:
        if userName == "张三" and passWord == '123456':
            return {"code": 0, "msg": "登录成功"}
        else:
            return {"code": 1, "msg": "登录失败了"}
    else:
        return {"code": 1, "msg": "所有的参数不能为空"}

然后基于账号为张三,密码为123456的情况下,去设计几个用例

三、编写用例

3.1 导入unittest模块

unittest是python自带的一个标准库,不需要进行第三方的安装,可以直接在py文件中导入

创建文件:test_开头

这里是模拟登录,调用封装的登录函数,所以命名可以以:test_login.py 进行命名

import unittest # 导入unittest库

3.2 创建测试类,该类继承unittest.TestCase

class TestLogin(unittest.TestCase):
    pass

3.3 定义测试函数,函数名以test_开头,一个函数表示一个用例

class TestLogin(unittest.TestCase):
    def test_login_success(self):  # 这个是登录成功的用例函数
        pass

3.4 编写用例

测试数据、期望结果、用例步骤、断言

什么是断言:我们的期望结果与实际结果的比对,而在调用函数后,得到的返回结果也就是实际结果

要调用对应的函数,就需要去把该函数进行一个导入

from login_func import login_check

在类中的方法调用函数

class TestLogin(unittest.TestCase):
    def test_login_success(self):  # 这个是登录成功的用例函数
        # 用例步骤 -- 用例数据
        act_result = login_check("张三", "123456")

调用函数后获取到返回值,这个返回值就是一个实际结果

 3.5 普通的比对表达(if)

在获取到实际结果以后,就去比对期望结果与实际结果

在上面的用例中,两个参数传递获取到的期望结果是登录成功的

而实际上,要返回的也应该是这个结果

class TestLogin(unittest.TestCase):
    def test_login_success(self):  # 这个是登录成功的用例函数
        # 用例步骤 -- 用例数据
        act_result = login_check("张三", "123456")
        # 断言 -- 实际结果 与 期望结果的 比对
        if act_result == {"code": 0, "msg": "登录成功"}:
            print("实际与期望相等!")
        else:
            print("实际与期望不相等!")

依次,就可以去照搬去写第二个方法的用例

    def test_login_failed_no_user(self):
        act_result = login_check("李四", "123456")

        # 断言 -- 实际结果 与 期望结果的 比对
        if act_result == {"code": 1, "msg": "登录失败了"}:
            print("实际与期望相等!")
        else:
            print("实际与期望不相等!")

(用户名错误、密码正确的情况)

四、断言assert

在上述的测试用例中,期望结果与实际结果的比对中是用if来进行

这个if与断言又有什么取别?

以上述的用例,在账号密码正确的用例中,修改if判断中比对的字典中,msg属性的值

if act_result == {"code": 0, "msg": "登录成功!!!"}:

进行测试的结果,执行的结果仍然是以用例通过为标准,但是输出的结果并不符合预期

那么,在if这个模式下,unittest并不会认为我们的用例是失败的

如果,把if转换成unittest的断言,应该如何编写

注释掉代码块中的if代码块

使用:self.assert....

在当前的用例中,需求是需要去比对:期望与返回值结果相等,所以使用断言:

self.assertEqual(self,first,second)

参数1:期望结果

参数2:实际结果

参数3:当实际与期望不相等时,自己希望得到的提示

修改代码

    def test_login_success(self):  # 这个是登录成功的用例函数
        # 用例步骤 -- 用例数据
        act_result = login_check("张三", "123456")
        print(act_result)
        # 断言 -- 实际结果 与 期望结果的 比对
        expected = {"code": 0, "msg": "登录成功!!!"}
        self.assertEqual(expected, act_result, "实际与期望不相等!!!")

测试的结果可以得到一个很明确的报错

AssertionError:断言错误

4.1 断言方法

方法

检查

说明

assertEqual(a,b)

a == b

检查变量a和b是否相等。如果不相等,测试将失败。

assertNotEqual(a,b)

a != b

检查变量a和b是否不相等。如果相等,测试将失败。

assertTrue(x)

bool(x) is True

参数可以为表达式、其他函数的调用,如果结果是true,则断言成功,false则失败

assertFalse(x)

Bool(x) is False

参数可以为表达式、其他函数的调用,如果结果是false,则断言成功,true则失败

assertIs(a,b)

a is b

is比较两个变量的内存地址是否一致,如一致,为true,断言成功,反之失败

assertIsNot(a,b)

a is not b

is比较两个变量的内存地址是否一致,如不一致,断言成功,反之一致则断言失败

assertIsNone(x)

x is None

比较变量x是否是None,如果是,则断言成功,反之失败

assertIsNotNone(x)

x is no None

比较变量x是否是None,如果不是,则断言成功,反之失败

assertIn(a,b)

a in b

比较a成员是否在b容器(字符串、列表、元组、元组、集合等)中的一员,如果是,则断言成功,反之失败

assertNotIn(a,b)

a not in b

比较a成员是否在b容器(字符串、列表、元组、元组、集合等)中的一员,如果不是,则断言成功,反之失败

assertIsInstance(a,b)

isinstance(a,b)

比较a是否是b的实例,如果属于b的实例,则断言通过

例如:变量a="hello",去判断变量a是否是str的实例,返回的结果是true,就断言成功了

assertNotIsInstance(a,b)

not isinstance(a,b)

比较a是否是b的实例,如果不属于b的实例,则断言通过

五、Fixture 夹具

  • 测试用例 级别的夹具
    • 测试用例的准备工作;setup()
    • 测试用例的清理工作:teardown()
  • 测试类 级别的夹具:
    • 测试类的准备工作:setupClass()
    • 测试类的清理工作:teardownClass()
    • 注意:在方法名上要加注解 @classmethod
  • 前置中的 数据传递 到测试用例当中去:
    • 通过设置实例属性、类属性来传递

5.1 测试用例的夹具

测试用例的准备工作;setup()

测试用例的清理工作:teardown()

在测试类中,def两个方法,一个是setup()、一个是tearDown()

    def setUp(self) -> None:
        print("我是准备工作....")

    def tearDown(self) -> None:
        print("我是清理工作")

此时,执行测试用例

用例是被夹子的准备工作和清除工作夹在中间,每一个用例都会被执行

setup()先执行 --> 测试用例 --> tearDown()

5.2 测试类的夹具

测试类的准备工作:setupClass()

测试类的清理工作:teardownClass()

注意:在方法名上要加注解 @classmethod

    @classmethod
    def setUpClass(cls) -> None:
        print("class 我是准备工作....")

    @classmethod
    def tearDownClass(cls) -> None:
        print("class 我是清理工作")

执行顺序:setUpClass() --> 用例1--> 用例2 --> tearDownClass()

5.3 前置工作的数据传递给测试用例

需求:在前置工作中的数据,如何作为参数传递给测试用例?

现在有一个测试类的夹具和一个测试方法夹具

    @classmethod
    def setUpClass(cls) -> None:
        print("class 我是准备工作....")

    def setUp(self) -> None:
        print("我是准备工作....")

那么,就需要设置成实例属性、类属性

5.3.1 setUp() 实例属性

    def setUp(self) -> None:
        print("我是准备工作....")
        self.name = "张三"
        self.pwd = "123456"

在方法中添加两个实例

在测试用例中,通过:self.xxx的方式访问

    def test_login_success(self):  # 这个是登录成功的用例函数
        act_result = login_check("张三", "123456")
        print("从前置当中获取准备好的变量")
        print(self.name)
        print(self.pwd)

5.3.2 setUpClass() 类属性

在这个前置方法中,需要定义为类属性

    @classmethod
    def setUpClass(cls) -> None:
        print("class 我是准备工作....")
        # cls.class_xxxx
        cls.class_name = "class_张三"

在测试用例的方法中,也直接通过self.变量名访问

        print("从class前置当中获取准备好的变量")
        print(self.class_name)

六、收集用例

测试用例会分布在不同的包、不同的模块(.py)中。

我们需要收集所有的测试用例,一并执行,并生成测试报告。

  • TestSuite类:测试用例集合
  • TestLoader类:测试用例收集

使用TestLoader类收集用例,加载到TestSuite类当中,最后执行TTestSuite中的用例即可

需求:把两个py文件中的测试用例,收集起来后,统一运行

代码:(把原有的py文件中,另外一个测试用例剪切到第二个py文件中)

模拟登录成功的py文件1

import unittest
from login_func import login_check


class TestLogin(unittest.TestCase):
    def test_login_success(self):  # 这个是登录成功的用例函数
        act_result = login_check("张三", "123456")
        expected = {"code": 0, "msg": "登录成功"}
        self.assertEqual(expected, act_result, "实际与期望不相等!!!")

模拟登录失败的py文件2

import unittest
from login_func import login_check


class TestNoLogin(unittest.TestCase):
    def test_login_failed_no_user(self):
        act_result = login_check("李四", "123456")
        expected = {"code": 1, "msg": "登录失败了"}
        self.assertEqual(expected, act_result, "实际与期望不相等!!!")

6.1 TestSuite套件

unittest.TestSuite()

添加用例到套件中的方法:

  • 方法一:addTest(测试类名("用例名"))
    • 添加一个测试用例
    • 每次只能添加一个
  • 方法二:addTests([测试类名("用例名"),测试类名("用例名")])
    • 添加一个用例的列表

第一步:独立创建一个py文件:main.py

导入:unittest

实例化TestSuite()

import unittest

s = unittest.TestSuite()

6.1.1 addTest()方法

实例化之后,调用addTest()方法,而参数是我们的测试用例,但是测试用例分布在其他的文件当中,所以需要对测试用例的类进行一个导入

# 导入测试用例
from test01 import TestLogin

语法格式:addTest(类名("用例名")

import unittest
# 导入测试用例
from test_login import TestLogin
from test_no_login import TestNoLogin
s = unittest.TestSuite()
s.addTest(TestLogin("test_login_success"))
s.addTest(TestNoLogin("test_login_failed_no_user"))
print(f"实例{s},类型{type(s)}")

6.1.2 addTests()方法

格式:addTests([测试类名("用例名"),测试类名("用例名")])

import unittest
# 导入测试用例
from test_login import TestLogin
from test_no_login import TestNoLogin
s = unittest.TestSuite()
s.addTests([TestLogin("test_login_success"),
           TestNoLogin("test_login_failed_no_user")])
print(f"实例{s},类型{type(s)}")

但是无论是addTest还是addTests,如果在用例过多的情况下,书写是非常麻烦的

所以,有了一个新的东西:TestLoader加载用例

6.2 TestLoader 加载用例

可以通过:类名、模块名、目录 三种方式去收集用例到测试套件中

  • 方法一:目录(常用)
    • unittest.TestLoader().discover(搜索目录)
      • 默认在:test*.py 中搜索用例
  • 方法二:类名
    • unittest.TestLoader().loadTestsFromTestCase(测试类名)
      • 注意:测试类名不需要加引号
  • 方法三:模块名
    • unittest.TestLoader().loadTestsFromModule(模块名)
      • 注意:模块名不需要加入引号(需要要导入模块)

6.2.1 目录 discover

在自动化的时候,用例会集中的写在某一个py包下面,所以对目录是比较具体化的

在目录下,会添加多个用例

所以通过指定的目录,去默认搜索全部的测试用例

但是在目录下面有很多个py文件,每个py文件也不一定都是测试用例

所以,需要对文件名也要有一定的过滤

默认:如果py的文件名以:test*.py开头,就会对这些文件进行搜索

Code

import unittest

# 1、实例化load
load = unittest.TestLoader()
# 2、使用discover方法:搜索指定目录,用变量接收结果
s = load.discover("test01")
# 3、打印出的s很长,可以用for循环来阅读
for i in s:
    print(i)
    print("\n")

6.2.2 从测试类中加载

格式:unittest.TestLoader().loadTestsFromTestCase(测试类名)

import unittest
from test_login import TestLogin
from test_no_login import TestNoLogin
# 1、实例化load
load = unittest.TestLoader()
# 2、从测试类中加载(在文件顶部需要先导入测试类)
s1 = load.loadTestsFromTestCase(TestLogin)
s2 = load.loadTestsFromTestCase(TestNoLogin)

这个时候有两个类,然后分开导入就有了两个套件

但是需求:把两个套件合并到一个套件里面去

把s1和s2合并到addTests

# 3、实例化测试套件
s = unittest.TestSuite()
s.addTests([s1, s2])
print(s)

6.2.3 从模块名中加载

格式:unittest.TestLoader().loadTestsFromModule(模块名)

也需要先导入模块,这里就不是导入类了        

import unittest
# 导入两个模块
import test_login
import test_no_login

# 1、实例化load
load = unittest.TestLoader()
# 2、从模块名当中进行导入
s1 = load.loadTestsFromModule(test_login)
s2 = load.loadTestsFromModule(test_no_login)
# 3、实例化测试套件
s = unittest.TestSuite()
s.addTests([s1, s2])  # 合并到大套件
print(s)

七、unittestreport - 生成HTML报告

使用unittestreport第三方库生成不同的HTML报告

第三方库地址:unittestreport · PyPIicon-default.png?t=N7T8https://pypi.org/project/unittestreport/

安装命令(支持py3.6+):pip install -U unittestreport

report库提供了三种不同的HTML测试报告样式

准备代码

import unittest
# 1、实例化load
load = unittest.TestLoader()
# 2、使用discover方法:搜索指定目录,用变量接收结果
s = load.discover("test01")

开始进行操作:

参考文档地址:二、HTML测试报告生成 - unittestreport 使用文档icon-default.png?t=N7T8https://unittestreport.readthedocs.io/en/latest/doc2_report/

1、导入TestRunner

from unittestreport import TestRunner

2、收集用例

# 1、实例化load
load = unittest.TestLoader()
# 2、使用discover方法:搜索指定目录,用变量接收结果
s = load.discover("test01")

3、运行用例,生成报告

# 3、运行用例,生成报告
runner = TestRunner(s)

4、执行run()方法,生成报告文件

# 4、执行run方法,生成报告
runner.run()

5、代码

import unittest
from unittestreport import TestRunner
# 1、实例化load
load = unittest.TestLoader()
# 2、使用discover方法:搜索指定目录,用变量接收结果
s = load.discover("test01")

# 3、运行用例,生成报告
runner = TestRunner(s)

# 4、执行run方法,生成报告
runner.run()

7.1 初始化参数

关于TestRunner初始化参数

在使用TestRunner创建测试运行程序时,可以通过以下参数来,自定义报告的相关内容

suites: 测试套件(必传)

filename: 指定报告文件名

report_dir:指定存放报告路径

title:指定测试报告的标题

templates: 可以指定1,2,3三个风格的模板

tester:测试人员名称

1、修改TestRunner()方法的参数

# 3、运行用例,生成报告
runner = TestRunner(s,
                    filename='模拟登录测试',
                    title="ChangFeng的测试报告",
                    templates=2,
                    tester='长风沛雨'
                    )

完整点的代码

import unittest
from unittestreport import TestRunner
# 1、实例化load
load = unittest.TestLoader()
# 2、使用discover方法:搜索指定目录,用变量接收结果
s = load.discover("test01")

# 3、运行用例,生成报告
runner = TestRunner(s,
                    filename='模拟登录测试',
                    title="ChangFeng的测试报告",
                    templates=2,
                    tester='长风沛雨'
                    )

# 4、执行run方法,生成报告
runner.run()

templates属性值的可选项为【1,2,3】对应了三种不同的样式风格

八、ddt数据驱动

ddt(data driven test) 数据驱动测试的设计思想

应用场景

        同一个流程,多组数据形成多条用例

Python第三方库:

使用 unittestreport库的ddt模块

ddt、json_data、list_data、yaml_data

注意点:测试报告中,用例的描述信息设置(title或者desc字段或者用例文档注释)

本文使用的是:unittestreport的数据驱动

8.1 list_data

举例使用:

1、创建一个文件来存储数据

创建文件:data.py

这里提供5组数据

all_case_data = [
    # 以字典存储,用户名、密码、期望结果
    {
        "title": "登录成功",
        "userName": "张三",
        "passWord": "123456",
        "msg": {"code": 0, "msg": "登录成功"}
    },
    {
        "title": "密码错误",
        "userName": "张三",
        "passWord": "123456789",
        "msg": {"code": 1, "msg": "登录失败了"}
    },
    {
        "title": "账号错误",
        "userName": "李四",
        "passWord": "123456",
        "msg": {"code": 1, "msg": "登录失败了"}
    },
    {
        "title": "账号为空",
        "userName": None,
        "passWord": "123456",
        "msg": {"code": 1, "msg": "所有的参数不能为空"}
    },
    {
        "title": "密码为空",
        "userName": "张三",
        "passWord": None,
        "msg": {"code": 1, "msg": "所有的参数不能为空"}
    }
]

2、创建一个test_use_ddt.py文件

新建的该文件是用来运行上面的数据来跑用例

然后创建一个类,该类基础TestCase,调用login_fuc的方法

import unittest

from login_func import login_check


class TestLoginPython(unittest.TestCase):
    def test_login(self):
        # 导入login_check()的方法
        act_result = login_check("张三", "123456")
        expected = {"code": 0, "msg": "登录成功"}
        self.assertEqual(expected, act_result, "实际与期望不相等!!!")

3、引入、装饰ddt

引入

from unittestreport import ddt, list_data

在类上装饰ddt

@ddt
class TestLoginPython(unittest.TestCase):

4、定义、接收测试数据

4.1 导入数据

from data import all_case_data

4.2 在用例方法上,把数据传给注解@list_data(),并在方法内用一个参数进行接收

@ddt
class TestLoginPython(unittest.TestCase):
    @list_data(all_case_data)
    def test_login(self, case):

而参数case,是接收到的每一组字典,那么,输入的数据就不用写死,只需要用到字典中的属性即可

4.3 调用数据

@ddt
class TestLoginPython(unittest.TestCase):
    @list_data(all_case_data)
    def test_login(self, case):
        # 导入login_check()的方法
        act_result = login_check(case.get("userName"), case.get("passWord"))
        self.assertEqual(case.get("msg"), act_result, "实际与期望不相等!!!")

数据获取语法:字典.get("属性")

5、该文件完整代码

import unittest

from login_func import login_check
from unittestreport import ddt, list_data
from data import all_case_data


@ddt
class TestLoginPython(unittest.TestCase):
    @list_data(all_case_data)
    def test_login(self, case):
        # 导入login_check()的方法
        act_result = login_check(case.get("userName"), case.get("passWord"))
        self.assertEqual(case.get("msg"), act_result, "实际与期望不相等!!!")

然后可以测试运行一下

一共有5条测试用例

而在用例的测试后缀中是添加了001开始的编号

但是,在测试的结果、测试的报告中,希望可以看到每个用例的含义

这个时候,在前面data.py文件中,字典里面的title属性就有了用处

6、返回main.py文件,生成测试报告

前面已经是写过了,不需要再修改了

import unittest
from unittestreport import TestRunner
# 1、实例化load
load = unittest.TestLoader()
# 2、使用discover方法:搜索指定目录,用变量接收结果
s = load.discover("test01")

# 3、运行用例,生成报告
runner = TestRunner(s,
                    filename='模拟登录测试',
                    title="ChangFeng的测试报告",
                    templates=2,
                    tester='长风沛雨'
                    )

# 4、执行run方法,生成报告
runner.run()

执行一下

8.2 json_data

如果需要用json_data来进行测试,就需要修改数据的格式,在前面8.1中,使用的是python的数据格式,这个json_data则需要修改为json格式

json与python数据类型的注意事项

json的写法:

字符串是双引号的

使用 null 来表示python中的 None

使用 true 和 false 表示布尔值

1、新建data.json文件

键入json类型的数据

[
  {
      "title": "登录成功",
      "userName": "张三",
      "passWord": "123456",
      "msg": {"code": 0, "msg": "登录成功"}
  },
  {
      "title": "密码错误",
      "userName": "张三",
      "passWord": "123456789",
      "msg": {"code": 1, "msg": "登录失败了"}
  },
  {
      "title": "账号错误",
      "userName": "李四",
      "passWord": "123456",
      "msg": {"code": 1, "msg": "登录失败了"}
  },
  {
      "title": "账号为空",
      "userName": null,
      "passWord": "123456",
      "msg": {"code": 1, "msg": "所有的参数不能为空"}
  },
  {
      "title": "密码为空",
      "userName": "张三",
      "passWord": null,
      "msg": {"code": 1, "msg": "所有的参数不能为空"}
  }
]

2、读取json数据

2.1 同样需要引入json.data

2.2 同样需要在用例上注解@json

2.3 注解方法传入的参数是json的相对路径或者绝对路径

import unittest
from login_func import login_check
from unittestreport import ddt, json_data


@ddt
class TestLoginPython(unittest.TestCase):
    @json_data(r"C:\Users\13195\Desktop\3ban_py\unittest\test01\data.json")
    def test_login(self, case):
        # 导入login_check()的方法
        act_result = login_check(case.get("userName"), case.get("passWord"))
        self.assertEqual(case.get("msg"), act_result, "实际与期望不相等!!!")

8.3 yaml

猜你喜欢

转载自blog.csdn.net/qq_63141957/article/details/134323036