Unittest

测试框架

基于 Python 语言的自动化测试框架 最知名的 有如下 3 款 测试框架:unittest,pytest,robotframework
前两款框架主要(或者说很大程度上)是 聚焦 在 白盒单元测试
而 robotframework 主要聚焦在 系统测试。

unittest

  1. 定义class使用Test开头,继承unittest.TestCase
  2. 定义test开头的方法

演示代码

import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
class TestCase(unittest.TestCase):
    driver = None
    def testPass(self):
        driver = webdriver.Edge(r'C:\Users\sj176\Downloads\edgedriver_win64\msedgedriver.exe')
        self.driver = driver
        self.driver.get('https://www.baidu.com')

运行方式
取决于右上角的运行环境,点击右键运行它默认会使用unittest环境运行,如果使用main运行则会走main方法否则不走
unittest运行用例的一个很大的坑:

  1. 命令行方式运行:python -m unittest 模块名.py 执行整个模块,或者使用python -m unittest 模块名.方法名执行某个方法
  2. main方式运行:建议再另写一个文件,写:
import unittest
if __name__ == '__main__':
    print('------------------')
    unittest.main()

Unittest框架

概念UnitTest是Python自带的一个单元测试框架,用它来做单元测试.
自带的框架:不需要安装,直接导报使用即可
第三方框架如果想使用需要进行安装如pytest
对于测试来说,unittest框架的作用是自动化脚本(用例代码)执行框架(使用unittest框架来管理 运行 多个测试用例)

  • 为什么使用UnitTest框架
  1. 能够组织多个用例去执行
  2. 提供丰富的断言方法
  3. 能够生成测试报告
  • UnitTest核心要素
  1. TestCase测试用例 最基本的操作用例
  2. TestSuite测试套件 用来管理,组装,打包TestCase用例的
  3. TestRunner测试执行,用来执行TestSuite(测试套件)
  4. TestLoader测试加载,功能是对TestSuite(测试套件)功能的补充
  5. Fixture测试夹具,书写在TestCase测试用例代码中,是一种代码结构,可以在每个方法前后都会执行的内容

TestCase测试用例

是一个代码文件,在代码文件中来书写真正的用例代码
代码文件名必须按照标识符的规则来书写
步骤:

  1. 导报(unittest)
  2. 定义测试类
  3. 在测试类中书写测试方法,方法名必须以test_开头
  4. 运行测试用例

代码:

import unittest

class TestDemo(unittest.TestCase):
    def test_method1(self):
        print('测试方法1')
    def test_method2(self):
        print('测试方法2')

    def test_method3(self):
        print('测试方法2')

# 执行方法,点击执行按钮或者输入

TestSuite和TestRunner

使用步骤

  1. 导报(unittest)
  2. 实例化(创建对象)套件对象
  3. 使用套件对象添加用例方法
  4. 实例化运行对象
  5. 使用运行对象执行套件对象

代码
方式一:
使用方法进行执行

import unittest
from Test1.SeleniumD import TestDemo

suite=unittest.TestSuite()
suite.addTest(TestDemo('test_method1'))
suite.addTest(TestDemo('test_method2'))
runner=unittest.TextTestRunner()
runner.run(suite)

方式二:
使用对象进行执行

import unittest

from Test1.SeleniumD import TestDemo
from Test1.SeleniumD2 import TestDemo2

suite=unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestDemo))
suite.addTest(unittest.makeSuite(TestDemo2))
runner=unittest.TextTestRunner()
runner.run(suite)

结果打印

.......
----------------------------------------------------------------------
Ran 7 tests in 0.000s

OK
测试方法1
测试方法1
测试方法2
测试方法3
测试方法11
测试方法22
测试方法33

其中.代表用例通过,F用例不通过,E用例代码有问题异常了

TestLoader(测试加载)

使用步骤

  1. 导包
  2. 实例化测试加载对象并添加用例--->得到的是suite对象
  3. 实例化运行对象
  4. 运行对象执行套件对象

代码实现

import unittest
# unittest.TestLoader().discover('用例所在的路径','用例的代码文件名')
suite=unittest.TestLoader().discover('./Test1','Se*.py')
runner=unittest.TextTestRunner()
runner.run(suite)

执行./Test1目录下的Se*.py测试文件

Fixture(测试夹具)

方法级别
在每个测试方法前后执行一次

class TestDemo2(unittest.TestCase):
    def setUp(self) -> None:
        print("setUp")

    def tearDown(self) -> None:
        print("tearDown")
    def test_method1(self):
        print('测试方法11')
    def test_method2(self):
        print('测试方法22')

    def test_method3(self):
        print('测试方法33')
结果:
setUp
测试方法11
tearDown
setUp
测试方法22
tearDown
setUp
测试方法33

类级别
在每个class前后执行一次

class TestDemo2(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass')
    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass')
    def test_method1(self):
        print('测试方法11')
    def test_method2(self):
        print('测试方法22')

    def test_method3(self):
        print('测试方法33')
结果:
setUpClass
测试方法11
测试方法22
测试方法33
tearDownClass

模块级别
在每个代码文件前后执行一次

import unittest

def setUpModule():
    print('setupModule')

# 执行方法,点击执行按钮或者输入
def tearDownModule():
    print('teardownModule')

class TestDemo2(unittest.TestCase):

    def test_method1(self):
        print('测试方法11')
    def test_method2(self):
        print('测试方法22')

    def test_method3(self):
        print('测试方法33')
class TestDemo3(unittest.TestCase):

    def test_method1(self):
        print('测试方法11')
    def test_method2(self):
        print('测试方法22')

    def test_method3(self):
        print('测试方法33')

结果:
setupModule
测试方法11
测试方法22
测试方法33
测试方法11
测试方法22
测试方法33
teardownModule

断言

什么是断言:让机器替代人来判断结果是否正确,断言结果:True,False
断言方法

  1. assertTrue(expr,msg=None):验证expr是true,如果为false,则false
  2. assertFalse(expr,msg=None):验证expr是false,如果为true,则false
  3. assertEqual(expr,msg=None):验证expr是==actual,不等于则false
  4. assertNotEqual(expr,msg=None):验证expr是!=actual相等则false
  5. assertNone(expr,msg=None):验证expr是None,不是则false
  6. assertNotNone(expr,msg=None):验证expr不是None,是则false
  7. assertIn(expr,container,msg=None):验证是否expr in container
  8. assertNotIn(expr,msg=None):验证是否expr not in container

assertIn('admin','adminnnnnn') 包含
assertIn('admin','ad1minnnnnn') 不包含
代码使用

class TestDemo2(unittest.TestCase):

    def test_method1(self):
        self.assertEqual('登录成功','登录成功啦')
    def test_method2(self):
        self.assertIn('登录成功','登录成功啦')

    def test_method3(self):
        self.assertEqual('登录成功','登录成功')

点击运行按钮运行如果想看测试报告可以点击控制台,左上角有个导出报告按钮,点击导出即可;

参数化

unittest框架本身是不支持参数化,要想使用参数化,需要安装插件来完成
需要安装pip install parameterized
如何验证是否安装成功使用pip list 查看是否存在
使用步骤

  1. 导包
  2. 定义测试类
  3. 书写测试方法(用到的测试数据使用变量代替)
  4. 组织测试数据并传参

代码

import unittest
from parameterized import parameterized

data=[
    ('登录成功','登录成功'),
    ('登录成功', '登录成功'),
    ('登录成功', '登录成功1'),
]


class TestLogin(unittest.TestCase):
    @parameterized.expand(data)
    def testLogin1(self,expect,retal):
        self.assertEqual(expect,retal)

结果:

C:\Users\sj176\AppData\Local\Programs\Python\Python310\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2022.1.2\plugins\python-ce\helpers\pycharm\_jb_pytest_runner.py" --target SeleniumD2.py::TestLogin
Testing started at 22:16 ...
Launching pytest with arguments SeleniumD2.py::TestLogin --no-header --no-summary -q in C:\Users\sj176\PycharmProjects\Demo3\Test1

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

SeleniumD2.py::TestLogin::testLogin1_0__ 
SeleniumD2.py::TestLogin::testLogin1_1__ 
SeleniumD2.py::TestLogin::testLogin1_2__ PASSED                          [ 33%]PASSED                          [ 66%]FAILED                          [100%]
SeleniumD2.py:11 (TestLogin.testLogin1_2__)
'登录成功1' != '登录成功'

Expected :'登录成功'
Actual   :'登录成功1'
<Click to see difference>

a = (<SeleniumD2.TestLogin testMethod=testLogin1_2__>,)

    @wraps(func)
    def standalone_func(*a):
>       return func(*(a + p.args), **p.kwargs)

..\..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\parameterized\parameterized.py:533: AssertionError




========================= 1 failed, 2 passed in 0.07s =========================

Process finished with exit code 1




跳过

对于一些未完成的或者不满足的测试条件的测试函数和测试类,不想执行,可以使用跳过装饰器来完成跳过该函数或这测试类
直接跳过
@unittest.skip('跳过原因')
根据条件判断是否跳过,判断条件成立-跳过
@unittest.skipIf('判断条件','跳过的原因')

import unittest

version=111
class TestDemo(unittest.TestCase):
    @unittest.skip('这个测试方法不能使用了')
    def test_method1(self):
        print('测试方法1')
    @unittest.skipIf(version>=30,'版本号大于等于30不用测试')
    def test_method2(self):
        print('测试方法2')

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

# 执行方法,点击执行按钮或者输入

测试报告

自带的测试报告
只有单独运行TestCase的代码才会生成测试报告
生成第三方的测试报告
安装pip install BeautifulReport
安装完成后注意如果使用右键执行不会输出测试报告应为执行方式是unittest需要使用python进行执行
python SeleniumD.py

# time :2021/1/22 15:29
# Author :Maynard

from BeautifulReport import BeautifulReport  # 需要导入BeautifulReport
import unittest
class TestDemoCase(unittest.TestCase):
    def setUp(self) -> None:
        pass

    def tearDown(self) -> None:
        pass

    def testassertdemo(self):
        self.assertEqual(1, 1)

def suite():
    # 创建一个测试套件
    suite = unittest.TestSuite()
    # 将测试用例加载到测试套件中
    loader = unittest.TestLoader() # 创建一个用例加载对象
    suite.addTest(loader.loadTestsFromTestCase(TestDemoCase))
    return suite

if __name__ == '__main__':
    br = BeautifulReport(suite())
    br.report(filename='testdemoreport.html',description='测试报告',log_path='.',report_dir='.')


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

推荐阅读更多精彩内容