Python——单元测试之unittest

一、 介绍

    1、框架

        说明:

            1. 框架英⽂单词framework 

            2. 为解决⼀类事情的功能集合

            > 需要按照框架的规定(套路) 去书写代码

    2、什么是UnitTest框架?

            概念:UnitTest是Python⾃带的⼀个单元测试框架,⽤它来做单元测试。

            ⾃带的框架(官⽅): 不需要单外安装, 只要安装了 Python,就可以使⽤

            例如:random, json, os, time

            第三⽅框架:想要使⽤ 需要先安装后使⽤(pytest)

            例如:selenium,appium,requests

            单元测试框架:主要⽤来做单元测试, ⼀般单元测试是开发做的.

            对于测试来说, unittest 框架的作⽤是 ⾃动化脚本(⽤例代码) 执⾏框架(使⽤ unittest 框架     来管理运⾏多个测试⽤例的)

    3、为什么使⽤UnitTest框架?

         1. 能够组织多个⽤例去执⾏

         2. 提供丰富的断⾔⽅法(让程序代码代替⼈⼯⾃动的判断预期结果和实际结果是否相符)

         3. 能够⽣成测试报告

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

        1、 TestCase(最核心的模块)

                1、TestCase(测试⽤例), 注意这个测试⽤例是 unittest 框架的组成部分, 不是⼿⼯和⾃动化中我们所说的⽤例(TestCase)

       主要作⽤: 每个 TestCase(测试⽤例) 都是⼀个代码⽂件, 在这个代码⽂件中 来书写 真正的⽤例代码

        2、TestSuite

                TestSuite(测试套件), ⽤来 管理 组装(打包)多个TestCase(测试⽤例) 的

        3、TestRunner

                 TestRunner(测试执⾏,测试运⾏), ⽤来执⾏TestSuite(测试套件)的

        4、TestLoader

                  TestLoader(测试加载), 功能是对 TestSuite(测试套件) 功能的补充,管理 组装(打包)多个 TestCase(测试⽤例) 的

        5、Fixture

                  Fixture(测试夹具), 书写在 TestCase(测试⽤例) 代码中, 是⼀个代码结构, 可以在每个⽅法执⾏前后都会执⾏的内容

                  例如:

                  登录的测试⽤例, 每个⽤例中重复的代码就可以写在Fixture 代码结构中, 只写⼀遍, 但每次⽤例⽅法的执⾏,都会执⾏ Fixture 中的代码

                  1. 打开浏览器

                  2. 输⼊⽹址

三、TestCase(测试用例)

        1. 是⼀个代码⽂件, 在代码⽂件中 来书写真正的⽤例代码

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

    步骤:

        1. 导包 (unittest)

        2. ⾃定义测试类

        3. 在测试类中书写测试⽅法

        4. 执⾏⽤例

    代码:

        """

        代码的⽬的: 学习 TestCase(测试⽤例)模块的书写⽅法

        """

        # 1, 导包

        import unittest

        # 2, ⾃定义测试类, 需要继承 unittest 模块中的TestCase 类即可

        class TestDemo(unittest.TestCase):

        # 3, 书写测试⽅法, 即 ⽤例代码. ⽬前没有真正的⽤例代码, 使⽤ print 代替

        # 书写要求, 测试⽅法 必须以 test_ 开头(本质是以test 开头)

        def test_method1(self):

            print('测试⽅法 1')

        def test_method2(self):

            print('测试⽅法 2')

        # 4, 执⾏⽤例(⽅法)

        # 4.1 将光标放在 类名的后边 运⾏, 会执⾏类中的所有的测试⽅法

        # 4.2 将光标放在 ⽅法名的后边 运⾏, 只执⾏当前的⽅法

问题⼀ 代码⽂件的命名不规范

        1. 代码⽂件的名字以数字开头

        2. 代码⽂件名字中有空格

        3. 代码⽂件名字有中⽂

        4. 其他的特殊符号

        (数字, 字⺟, 下划线组成, 不能以数字开头)

问题 2 代码运⾏没有结果

        右键运⾏没有 unittests for 的提示, 出现的问题

        解决⽅案:

        ⽅案1. 重新新建⼀个代码⽂件, 将写好的代码复制进去

        ⽅案2. 删除已有的运⾏⽅式

问题 3 没有找到⽤例

        测试⽅法中不是以 test_ 开头的, 或者单词写错了

四、TestSuite & TestRunner

    TestSuite(测试套件): 管理 打包 组装 TestCase(测试⽤例)⽂件的

    TestRunner(测试执⾏): 执⾏ TestSuite(套件)

    步骤:

        1. 导包(unittest)

        2. 实例化(创建对象)套件对象

        3. 使⽤套件对象添加⽤例⽅法

        4. 实例化运⾏对象

        5. 使⽤运⾏对象去执⾏套件对象

    代码:

        TestSuite(测试套件): 是⽤来管理多个 TestCase(测试⽤例) 的,

        先创建多个 TestCase(测试⽤例) ⽂件

        """

        学习 TestSuite 和 TestRunner 的使⽤

        """

        # 1. 导包(unittest)

        import unittest

        from hm_07_testcase1 import TestDemo1

        from hm_07_testcase2 import TestDemo2

        # 2. 实例化(创建对象)套件对象,

        suite = unittest.TestSuite()

        # 3. 使⽤套件对象添加⽤例⽅法

        # ⽅式⼀, 套件对象.addTest(测试类名('⽅法名')) 

        #  建议测试类名和⽅法名直接去复制,不要⼿写

        suite.addTest(TestDemo1('test_method1'))

        suite.addTest(TestDemo1('test_method2'))

        suite.addTest(TestDemo2('test_method1'))

        suite.addTest(TestDemo2('test_method2'))

        # 4. 实例化运⾏对象

        runner = unittest.TextTestRunner()

        # 5. 使⽤运⾏对象去执⾏套件对象

        # 运⾏对象.run(套件对象)

        runner.run(suite)

        

        """

        学习 TestSuite 和 TestRunner 的使⽤

        """

        # 1. 导包(unittest)

        import unittest

        from hm_07_testcase1 import TestDemo1

        from hm_07_testcase2 import TestDemo2

        # 2. 实例化(创建对象)套件对象,

        suite = unittest.TestSuite()

        # 3. 使⽤套件对象添加⽤例⽅法

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

        # 套件对象.addTest(unittest.makeSuite(测试类名))

        # 缺点: makeSuite() 不会提示

        suite.addTest(unittest.makeSuite(TestDemo1))

        suite.addTest(unittest.makeSuite(TestDemo2))

        # 4. 实例化运⾏对象

        runner = unittest.TextTestRunner()

        # 5. 使⽤运⾏对象去执⾏套件对象

        # 运⾏对象.run(套件对象)

        runner.run(suite)

五、TestLoader (测试加载)

TestLoader (测试加载), 作用和 TestSuite 的作用是一样的, 对 TestSuite 功能的补充,

用来组装测试用例的

比如: 如果 TestCase 的代码文件有很多, (10 20, 30 )

- 使用步骤

1. 导包

2. 实例化测试加载对象并添加用例 ---> 得到的是 suite 对象

3. 实例化 运行对象

4. 运行对象执行套件对象

代码实现:

    在一个项目中 TestCase(测试用例) 的代码,一般放在一个单独的目录 (case)

"""

TestLoader 的使用

"""

# 1, 导包

import unittest

# 2, 实例化加载对象并添加用例

# unittest.TestLoader().discover('用例所在的路径', '用例的代码文件名')

# 用例所在的路径,建议使用相对路径, 用例的代码文件名可以使用 *(任意多个任意字符) 通配符

# suite = unittest.TestLoader().discover('./case', 'hm*.py')

# suite = unittest.TestLoader().discover('./case', '*test*.py')

# suite = unittest.TestLoader().discover('./case', '*test*')

suite = unittest.TestLoader().discover('./case', '*case1.py')

# 3, 实例化运行对象

# runner = unittest.TextTestRunner()

# # 4, 执行

# runner.run(suite)

# 可以将 3 4 步 变为一步

unittest.TextTestRunner().run(suite)


# 1. 导包

# 2. 使用默认的加载对象并加载用例

# 3. 实例化运行对象并运行

"""TestLoader 的使用"""

# 1, 导包

import unittest

# 2, 使用默认的加载对象并加载用例

suite = unittest.defaultTestLoader.discover('case', 'hm_*.py')

# 可以将 3 4 步 变为一步

unittest.TextTestRunner().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

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

案例:

    1. 打开浏览器(整个测试过程中就打开一次浏览器) 类级别

    2. 输入网址(每个测试方法都需要一次) 方法级别

    3. 输入用户名密码验证码点击登录(不同的测试数据) 测试方法

    4. 关闭当前页面(每个测试方法都需要一次) 方法级别

    5. 关闭浏览器(整个测试过程中就关闭一次浏览器) 类级别


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:

    self.assertEqual(预期结果, 实际结果) # 判断预期结果和实际结果是否相等

    1. 如果相等, 用例通过

    2. 如果不相等,用例不通过, 抛出异常

assertIn:

    self.assertIn(预期结果, 实际结果) # 判断预期结果是否包含在实际结果中

    1. 包含 ,用例通过

    2. 不包含, 用例不通过, 抛出异常

assertIn('admin', 'admin') # 包含

assertIn('admin', 'adminnnnnnnn') # 包含

assertIn('admin', 'aaaaaadmin') # 包含

assertIn('admin', 'aaaaaadminnnnnnn') # 包含

assertIn('admin', 'addddddmin') # 不是包含

代码:

import unittest

from 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, 登录失败"""

# self.assertEqual('登录失败', login('aaa', '123123'))

self.assertIn('失败', login('aaa', '123123'))

八、参数化

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

好处: 相似的代码不需要多次书写.

工作中场景:

1. 测试数据一般放在 json 文件中

2. 使用代码读取 json 文件,提取我们想要的数据 ---> [(), ()] or [[], []]

安装插件    

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

- 联网安装(在 cmd 窗口安装 或者 )

    pip install parameterized

pip 是 Python 中包(插件) 的管理工具, 使用这个工具下载安装插件

验证

pip list  # 查看到 parameterized

参数化代码

1. 导包 unittest/ pa 

2. 定义测试类 

3. 书写测试方法(用到的测试数据使用变量代替) 

4. 组织测试数据并传参


例子

参数化 2

[{"desc": "正确的用户名和密码","username": "admin","password": "123456","expect": "登录成功"},{"desc": "错误的的用户名","username": "root","password": "123456","expect": "登录失败"},{"desc": "错误的的密码","username": "admin","password": "123123","expect": "登录失败"}]

# 1、导包 unittest/ pa

import json

import unittest

from parameterized import parameterized

from tools import login

# 组织测试数据 [(), (), ()] or [[], [], []]

def build_data():

with open('data.json', encoding='utf-8') as f:

result = json.load(f) # [{}, {}, {}]

data = []

for i in result: # i {}

data.append((i.get('username'), i.get('password'), i.get('expect')))

return data

# 2. 定义测试类

class TestLogin(unittest.TestCase):

# 3. 书写测试方法(用到的测试数据使用变量代替)

@parameterized.expand(build_data())

def test_login(self, username, password, expect):

    self.assertEqual(expect, login(username, password))

# 4. 组织测试数据并传参(装饰器 @)

八、跳过

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

使用方法, 装饰器完成,代码书写在 TestCase 文件

# 直接将测试函数标记成跳过

@unittest.skip('跳过额原因')

# 根据条件判断测试函数是否跳过 , 判断条件成立, 跳过

@unittest.skipIf(判断条件, '跳过原因')


import unittest

# version = 30

version = 29

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 的代码,才会生成测试报告

生成第三方的测试报告

1. 获取第三方的 测试运行类模块 , 将其放在代码的目录中

2. 导包 unittest

3. 使用 套件对象, 加载对象 去添加用例方法

4. 实例化 第三方的运行对象 并运行 套件对象


# 1. 获取第三方的 测试运行类模块 , 将其放在代码的目录中

# 2. 导包 unittest

import unittest

from HTMLTestRunner import HTMLTestRunner

# 3. 使用 套件对象, 加载对象 去添加用例方法

suite = unittest.defaultTestLoader.discover('.', 'hm_05_pa1.py')

# 4. 实例化 第三方的运行对象 并运行 套件对象

# HTMLTestRunner()

# stream=sys.stdout, 必填,测试报告的文件对象(open ), 注意点,要使用 wb 打开

# verbosity=1, 可选, 报告的详细程度,默认 1 简略, 2 详细

# title=None, 可选, 测试报告的标题

# description=None 可选, 描述信息, Python 的版本, pycharm 版本

# file = 'report.html' # 报告的后缀是.html

file = 'report1.html' # 报告的后缀是.html

with open(file, 'wb') as f:

# runner = HTMLTestRunner(f) # 运行对象

runner = HTMLTestRunner(f, 2, '测试报告', 'python 3.6.8 ') # 运行对象

# 运行对象执行套件, 要写在 with 的缩进中

runner.run(suite)


1. 组织用例文件(TestCase 里边), 书写参数化, 书写断言, 书写 Fixture, 书写 跳过, 如果单个测试测试文

件, 直接运行, 得到测试报告, 如果有多个测试文件, 需要组装运行生成测试报告

2. 使用 套件对象组装, 或者使用 加载对象组装

3. 运行对象 运行

3.1 运行对象 = 第三方的运行类(文件对象(打开文件需要使用 wb 方式))

3.2 运行对象.run(套件对象)

import unittest

from HTMLTestRunnerCN import HTMLTestReportCN

# 组装用例方法

suite = unittest.defaultTestLoader.discover('.', '*pa1.py')

# 实例化运行对象

with open('report_cn.html', 'wb') as f:

runner = HTMLTestReportCN(f)

runner.run(suite)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 一、单元测试、集成测试、功能测试单元测试颗粒度最小,一般由开发小组采用白盒方式来测试,主要测试单元是否符合“设计”...
    老友_9e40阅读 298评论 0 0
  • 目录 unittest 类容 单元测试,集成测试,功能测试 Unittest的重要组成部分 断言的使用 测试用例方...
    淺色年華_7d4f阅读 242评论 0 0
  • 目录类容 unittest单元测试,集成测试,功能测试 Unittest的重要组成部分 断言的使用 测试用例方法的...
    601eff46fb64阅读 143评论 0 0
  • 一:单元测试,集成测试,功能测试 单元测试 颗粒度最小,一般由开发小组采用白盒方式来测试,主要测试单元是否符合“设...
    Liqingxiang阅读 174评论 0 0
  • 一、单元测试、集成测试、功能测试 单元测试 颗粒度最小,一般由开发小组采用白盒方式来测试,主要测试单元是否符合“设...
    我向星明许愿阅读 163评论 0 0