笔记:Python测试——UnitTest

目录

UnitTest框架基础

TestCase(测试⽤例)

TestSuite & TestRunner

TestLoader (测试加载)

Fixture(测试夹具)

断言

参数化

跳过

测试报告


UnitTest框架基础

UnitTest是Python⾃带的⼀个单元测试框架,⽤它来做单元测试,不需要单外安装。

单元测试框架: 主要⽤来做单元测试, ⼀般单元测试是开发做的。
对于测试来说, unittest 框架的作⽤是⾃动化脚本(⽤例代码) 执⾏框架(使⽤ unittest 框架来管理运⾏多个测试⽤例的)

为什么使⽤UnitTest框架:

1. 能够组织多个⽤例去执⾏。
2. 提供丰富的断⾔⽅法(让程序代码代替⼈⼯⾃动的判断预期结果和实际结果是否相符)。
3. 能够⽣成测试报告。

UnitTest核心要素(unitest 的组成部分):

1. TestCase(最核⼼的模块):TestCase(测试⽤例), 这个测试⽤例是 unittest 框架的组成部分, 不是⼿⼯和⾃动化中我们所说的⽤例(Test Case)。
主要作⽤: 每个 TestCase(测试⽤例) 都是⼀个代码⽂件, 在这个代码⽂件中书写真正的⽤例代码。
2. TestSuite:TestSuite(测试套件), ⽤来管理组装(打包)多个TestCase(测试⽤例) 。
3. TestRunner:TestRunner(测试执⾏,测试运⾏), ⽤来执⾏TestSuite(测试套件)。
4. TestLoader:TestLoader(测试加载), 功能是对 TestSuite(测试套件)功能的补充,管理,组装(打包)多个TestCase(测试⽤例) 的
5. Fixture:Fixture(测试夹具), 书写在 TestCase(测试⽤例) 代码中, 是⼀个代码结构, 可以在每个⽅法执⾏前后都会执⾏的内容。

TestCase(测试⽤例)

        TestCase(测试⽤例)代码⽂件的名字必须按照标识符的规则来书写(可以将代码的作⽤在⽂件的开头使⽤注释说明)。

        例子:

先定义一个用来测试的add_test.py文件:

#add函数
def add(a,b):
    c =a + b
    return  c

下面是TestCase(测试⽤例)代码test_add_test.py文件:

"""
代码的⽬的: 学习 TestCase(测试⽤例)模块的书写⽅法
"""
# 导包
import unittest
# 导入测试代码
from add_test import add

# 测试add_test的类
# ⾃定义测试类, 需要继承 unittest 模块中的TestCase 类即可
class addtest(unittest.TestCase):
    # 书写要求:测试⽅法 必须以 test_ 开头(本质是以test 开头)
    # 学习断言后不用if和print做判断
    def test_add(self):
        if add(1,2) == 3:
            print("测试通过")
        else:
            print("测试不通过")

    def test_add02(self):
        if add(1,2) == 6:
            print("测试通过")
        else:
            print("测试不通过")

    def test_add03(self):
        if add(2.1,2) == 4.1:
            print("测试通过")
        else:
            print("测试不通过")

# 执⾏⽤例(⽅法)
# 将光标放在 类名的后边运⾏, 会执⾏类中的所有的测试⽅法
# 将光标放在 ⽅法名的后边运⾏, 只执⾏当前的⽅法

下面是测试结果:

如果不能运行问题:

1.代码⽂件的命名不规范,必须按照标识符的规则来书写。

2.代码运⾏没有结果右键运⾏没有 unittests for 的提示(将光标放在类名的后边运⾏, 会执⾏类中的所有的测试⽅法。将光标放在 ⽅法名的后边运⾏, 只执⾏当前的⽅法)。解决⽅案:(1)重新新建⼀个代码⽂件, 将写好的代码复制进去(2)删除已有的运⾏⽅式。

3.没找到用例,测试⽅法中不是以 test_ 开头的, 或者单词写错了。

TestSuite & TestRunner

TestSuite(测试套件):管理,打包,组装TestCase(测试⽤例)
TestRunner(测试执⾏):执⾏TestSuite(套件)

下面是将TestCase(测试⽤例)代码打包运行add_test_suite&runner.py文件:

"""
TestSuite 和 TestRunner 的使⽤
"""
# 导包(unittest)
import unittest
# 导入testcase文件
from test_add_test import addtest

# 实例化(创建对象)套件对象
suite = unittest.TestSuite()

# 套件对象.addTest(测试类名('⽅法名'))
suite.addTest(addtest('test_add'))
suite.addTest(addtest('test_add02'))
suite.addTest(addtest('test_add03'))

# 实例化运⾏对象
runner = unittest.TextTestRunner()
# 使⽤运⾏对象去执⾏套件对象
# 运⾏对象.run(套件对象)
runner.run(suite)

⽅式⼆:将⼀个测试类中的所有⽅法进⾏添加

import unittest
from test_add_test import addtest

# 实例化(创建对象)套件对象
suite = unittest.TestSuite()

# 将⼀个测试类中的所有⽅法进⾏添加
# 套件对象.addTest(unittest.makeSuite(测试类名))
# 缺点: makeSuite() 不会提示
suite.addTest(unittest.makeSuite(addtest))

# 实例化运⾏对象
runner = unittest.TextTestRunner()
# 运⾏对象去执⾏套件对象
runner.run(suite)

TestLoader (测试加载)

TestLoader (测试加载), 作用和 TestSuite 的作用是一样的, 对 TestSuite 功能的补充,用来组装测试用例的。当TestCase 的代码文件有很多的时候,使用TestLoader (测试加载)。

import unittest

#  实例化加载对象并添加用例
# unittest.TestLoader().discover('用例所在的路径', '用例的代码文件名')
# 用例所在的路径,建议使用相对路径, 用例的代码文件名可以使用 *(任意多个任意字符) 通配符
# 可以执行同一个路径下的多个测试用例文件
# 下面代码意思是当前路径下以 test_ 开头的所有TsetCase(测试用例)文件
suite = unittest.TestLoader().discover(".","test_*.py")

# 实例化运行对象
runner = unittest.TextTestRunner()
# 执行
runner.run(suite)

Fixture(测试夹具)

Fixture(测试夹具) 是一种代码结构,在某些特定的情况下会自动执行。

方法级别(掌握):在每个测试方法(用例代码) 执行前后都会自动调用的结构。

# 方法执行之前
def setUp(self):
每个测试方法执行之前都会执行
pass
# 方法执行之后
def tearDown(self):
每个测试方法执行之后都会执行
pass

类级别(掌握):在每个测试类中所有方法执行前后都会自动调用的结构(在整个类,执行前执后各一次)

# 类级别的Fixture 方法, 是一个 类方法
# 类中所有方法之前
@classmethod
def setUpClass(cls):
pass
# 类中所有方法之后
@classmethod
def tearDownClass(cls):
pass

模块级别(了解):模块(代码文件)在每个代码文件执行前后执行的代码结构。

# 模块级别的需要写在类的外边直接定义函数即可
# 代码文件之前
def setUpModule():
pass
# 代码文件之后
def tearDownModule():
pass

方法级别和类级别的 前后的方法,不需要同时出现,根据用例代码的需要自行的选择使用。

案例:登录测试。test_Fixture.py文件。

import unittest

class TestLogin(unittest.TestCase):
    def setUp(self):
        # 每个测试方法执行之前都会先调用的方法
        print('输入网址......')

    def tearDown(self) -> None:
        # 每个测试方法执行之后都会调用的方法
        print('关闭当前页面......')

    @classmethod
    # 所有方法执行前会自动调用一次
    def setUpClass(cls) -> None:
        print('------1. 打开浏览器')

    @classmethod
    # 所有方法执行后会自动调用一次
    def tearDownClass(cls) -> None:
        print('------5. 关闭浏览器')

    def test_1(self):
        # 普通测试
        print('输入正确用户名密码验证码,点击登录 1')

    def test_2(self):
        # 普通测试
        print('输入错误用户名密码验证码,点击登录 2')

下面是运行结果:

断言

让程序代替人工自动的判断预期结果和实际结果是否相符。
断言的结果有两种:
> True, 用例通过
> False, 代码抛出异常, 用例不通过
在 unittest 中使用断言, 都需要通过 self.断言方法来试验

方法检查    描述
assertEqual(a, b) a == b验证a是否等于b
assertIn(a, b)a in b 验证a是否是b的子串

这里简单示范两个,用法都一样。更多断言可以上网查找。

先定义一个用来测试的test_tools.py文件:

def login(username, password):
    if username == 'admin' and password == '123456':
        return '登录成功'
    else:
        return '登录失败'

下面是断言代码test_assert.py文件:

import unittest
from test_tools import login

class TestLogin(unittest.TestCase):
    def test_username_password_ok(self):
        """正确的用户名和密码: admin, 123456, 登录成功"""
        self.assertEqual('登录成功', login('admin', '123456'))

    def test_username_error(self):
        """错误的用户名: root, 123456, 登录失败"""
        self.assertEqual('登录失败', login('root', '123456'))

    def test_password_error(self):
        """错误的密码: admin, 123123, 登录失败
            这里断言失败,应该是登录失败
        """
        self.assertEqual('登录成功', login('admin', '123123'))

    def test_username_password_error(self):
        """错误的用户名和错误的密码: aaa, 123123, 登录失败
           这里断言成功是因为  assertIn  验证a是否是b的子串
           失败是登录失败的子串
        """
        self.assertIn('失败', login('aaa', '123123'))

下面是断言测试结果:

这里有一个断言失败,第17行,原因注释有写。

参数化

参数化:在测试方法中, 使用变量来代替具体的测试数据, 然后使用传参的方法将测试数据传递给方法的变量。

unittest 框架本身是不支持参数化, 想要使用参数化,需要安装插件来完成(cmd窗口)

pip install parameterized

在实际工作中测试数据一般放在 json 文件中,使用代码读取 json 文件,提取我们想要的数据 为这种形式---> [(), ()] or [[], []]

下面是参数化代码文件:

import unittest
from parameterized import parameterized
from test_tools import login

# 组织测试数据  [(), (), ()] or [[], [], []]
data = [
    ('admin', '123456', '登录成功'),
    ('root', '123456', '登录失败'),
    ('admin', '123123', '登录失败')
]
# 定义测试类
class TestLogin(unittest.TestCase):
    # 书写测试方法(用到的测试数据使用变量代替)
    # 组织测试数据并传参(装饰器 @)
    # 数据和参数一一对应
    @parameterized.expand(data)
    def test_login(self, username, password, expect):
        self.assertEqual(expect, login(username, password))

跳过

对于一些未完成的或者不满足测试条件的测试函数和测试类, 不想执行,可以使用跳过。使用方法, 装饰器完成。代码书写在 TestCase 文件中。

# 直接将测试函数标记成跳过
@unittest.skip('跳过额原因')
# 根据条件判断测试函数是否跳过 , 判断条件成立, 跳过
@unittest.skipIf(判断条件, '跳过原因')

下面是跳过的示例test_skip.py

import unittest

# version = 29
version = 31

class TestDemo(unittest.TestCase):

    # 直接将测试函数标记成跳过
    @unittest.skip('没有什么原因,就是不想执行')
    def test_1(self):
        print('测试方法 1')

    # 根据条件判断测试函数是否跳过 , 判断条件成立, 跳过
    @unittest.skipIf(version >= 30, '版本大于等于 30, 不用测试')
    def test_2(self):
        print('测试方法 2')

    def test_3(self):
        print('测试方法 3')

下面是测试运行结果:

测试报告

自带的测试报告:只有单独运行 TestCase 的代码,才会生成测试报告。(比较简陋)

在pycharm中鼠标点三个点,可以看见。老版本将运行窗口往右拉可以看见。

生成第三方的测试报告:获取第三方的 测试运行类模块 , 将其放在代码的目录中。

需要HTMLTestRunner.py文件或者HTMLTestRunnerCN.py(中文版)文件

下面是中文版测试报告示例test_report_cn.py文件

import unittest
from HTMLTestRunnerCN import HTMLTestReportCN

# 使用套件对象, 加载对象 去添加用例方法
suite = unittest.defaultTestLoader.discover('.', 'test_assert.py')
# 4. 实例化 第三方的运行对象 并运行 套件对象
# HTMLTestRunner()
# stream=sys.stdout, 必填,测试报告的文件对象(open ), 注意点,要使用 wb 打开
# verbosity=1, 可选, 报告的详细程度,默认 1 简略, 2 详细
# title=None, 可选, 测试报告的标题
# description=None 可选, 描述信息, Python 的版本, pycharm 版本

file = 'report.html'   # 报告的后缀是.html
with open(file, 'wb') as f:
    # runner = HTMLTestRunner(f)  # 运行对象
    runner = HTMLTestReportCN(f, 2, '测试报告', 'python 3.6.8 ')  # 运行对象

    # 运行对象执行套件, 要写在 with 的缩进中
    runner.run(suite)

得到report.html文件,用浏览器打开:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值