unittest

unittest

1. 基础

unittest —— 单元测试框架,这里的单元测试指的是对最小的软件设计单元(模块)进行验证,在UI自动化测试里面,单元测试主要针对UI界面的功能进行自动化测试

unittest 是python 的单元测试框架,它主要有以下作用:

  1. 提供用例组织与执行
  2. 提供丰富的比较方法
  3. 提供丰富的日志

unittest 四个重要概念

  1. Test Fixture:对一个测试用例环境的搭建和销毁,就是一个fixture,通过覆盖 setUp() 和 tearDown() 方法来实现
    setUp() 方法可以进行初始化,比如测试环境的搭建,获取待测试浏览器的驱动
    tearDown() 方法进行环境的销毁,可以进行关闭浏览器,关闭数据库连接,清除数据库中产生的数据等操作
  2. Test Case:一个 TestCase 的实例就是一个测试用例。单元测试的本质就在这里,一个测试用例就是一个完整的测试单元,可以对某一个功能进行验
  3. Test Suite:一个功能的验证往往需要多个测试用例,可以把多个测试用例集合在一起执行,这个就产生了测试套件TestSuite的概念
  4. Test Runner:在unittest框架中,通过 TextTestRunner 类提供的 run() 方法来执行 test suite/test case

2. 四个重要概念

1. TestFixture

TestFixture :对一个测试用例环境的搭建和销毁,就是一个fixture,通过覆盖 setUp() 和 tearDown() 方法来实现

1.1 setUp() 和 tearDown()
from selenium.webdriver.common.by import By
from selenium import webdriver
import unittest
import time

'''
TestFixture: setUp和tearDown
    setUp()方法: 主要是用来初始化测试环境, 它在每条测试用例执行前都会调用
    tearDown()方法:主要作用是测试用例执行完毕后恢复测试环境, 即使出现异常也会调用此方法,每条用例执行结束后都会运行
'''


class Test(unittest.TestCase):
    def setUp(self):
        print("-----setUp:初始化测试环境-----")
        self.driver = webdriver.Chrome()
        self.url = "https://www.baidu.com/"
        time.sleep(3)

    def test_baidu01(self):
        driver = self.driver
        url = self.url
        driver.get(url)
        driver.find_element(By.LINK_TEXT, "hao123").click()
        time.sleep(4)

    def test_baidu02(self):
        driver = self.driver
        url = self.url
        driver.get(url)
        driver.find_element(By.LINK_TEXT, "新闻").click()
        time.sleep(4)

    def tearDown(self):
        print("-----tearDown:测试用例执行完毕后恢复测试环境-----")
        self.driver.quit()
import unittest

class Test(unittest.TestCase):
    def setUp(self):  # 调用setUp
        super().setUp()
        print("用例执行前操作")

    def test_add01(self):
        print(2+2)

    def test_add02(self):
        print(3+3)

    def tearDown(self):  # 调用tearDown
        super().tearDown()
        print("用例执行后操作")
1.2 setUpClass() 和 tearDownClass()
from selenium.webdriver.common.by import By
from selenium import webdriver
import unittest
import time

'''
setUpClass: 初始化测试环境且只会执行一次。在类中需要加上@classmethod
tearDownClass: 恢复测试环境且只会执行一次。在类中需要加上@classmethod
'''

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        print("-----setUp:初始化测试环境-----")
        self.driver = webdriver.Chrome()
        self.url = "https://www.baidu.com/"
        time.sleep(3)

    def test_baidu01(self):
        driver = self.driver
        url = self.url
        driver.get(url)
        driver.find_element(By.LINK_TEXT, "hao123").click()
        time.sleep(4)

    def test_baidu02(self):
        driver = self.driver
        url = self.url
        driver.get(url)
        driver.find_element(By.LINK_TEXT, "新闻").click()
        time.sleep(4)

    @classmethod
    def tearDownClass(self):
        print("-----tearDown:测试用例执行完毕后恢复测试环境-----")
        self.driver.quit()
import unittest

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        print("用例执行前操作")

    @classmethod
    def tearDownClass(cls):
        super().tearDownClass()
        print("用例执行后操作")

    def test_add01(self):
        print(2+2)

    def test_add02(self):
        print(3+3)

2. TestCase

TestCase 指的就是测试用例
TestCase编写:

  1. 测试类必须继承 unittest.TestCase
  2. 测试方法名称命名必须以 test_ 开头
  3. 测试方法的执行顺序由 Case序号 决定, 并非由代码顺序决定
# 1. 导入unittest
import unittest

'''
TestCase
'''

# 2. 创建类继承unittest.TestCase
class Test(unittest.TestCase):
    # 3. 创建测试用例方法, 方法要以 test_ 开头
    # 执行顺序根据case序号决定, 并非代码的顺序  0~9 A~Z a~z
    def test_add01(self):
        print(2 + 2)

    def test_add02(self):
        print(3 + 3)

3. TestSuite

TestSuite(测试套件): 可以组织多个测试用例

先准备两个测试用例:test16001 和 test16002

# test16001
import unittest

class Test01(unittest.TestCase):
    def setUp(self):  # 调用setUp
        super().setUp()
        print("用例执行前操作")

    def test_add01(self):
        print(1+1)

    def test_add02(self):
        print(2+2)

    def tearDown(self):  # 调用tearDown
        super().tearDown()
        print("用例执行后操作")
# test16002
import unittest


class Test02(unittest.TestCase):
    def setUp(self):  # 调用setUp
        super().setUp()
        print("用例执行前操作")

    def test_add03(self):
        print(3+3)

    def test_add04(self):
        print(4+4)

    def tearDown(self):  # 调用tearDown
        super().tearDown()
        print("用例执行后操作")
        
3.1 addTest()

把不同测试脚本的类中需要执行的方法放在一个测试套件中

import unittest
from Selenium学习 import test16001
from Selenium学习 import test16002


def creatSuit():

    # 把不同测试脚本的类中需要执行的方法放在一个测试套件中
    # addTest
    suite = unittest.TestSuite()
    suite.addTest(test16001.Test01("test_add01"))
    suite.addTest(test16002.Test02("test_add04"))
    return suite

if __name__ == "__main__":
    suit = creatSuit()
    runner = unittest.TextTestRunner()
    runner.run(suit)
3.2 makeSuit()

把一个测试脚本中所有的测试用例都添加到测试套件中

import unittest
from Selenium学习 import test16001
from Selenium学习 import test16002


def creatSuit():
    # 把一个测试脚本中所有的测试用例都添加到测试套件中
    # makeSuit
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(test16001.Test01))
    suite.addTest(unittest.makeSuite(test16002.Test02))
    return suite

if __name__ == "__main__":
    suit = creatSuit()
    runner = unittest.TextTestRunner()
    runner.run(suit)
3.3 TestLoader()

把一个测试脚本中所有的测试用例都添加到测试套件中

import unittest
from Selenium学习 import test16001
from Selenium学习 import test16002


def creatSuit():
    # 把一个测试脚本中所有的测试用例都添加到测试套件中
    # TestLoader
    suit1 = unittest.TestLoader().loadTestsFromTestCase(test16001.Test01)
    suit2 = unittest.TestLoader().loadTestsFromTestCase(test16002.Test02)
    suite = unittest.TestSuite([suit1, suit2])
    return suite

if __name__ == "__main__":
    suit = creatSuit()
    runner = unittest.TextTestRunner()
    runner.run(suit)
3.4 discover

把一个文件夹下面所有的测试脚本中的测试用例放入测试套件

import unittest
from Selenium学习 import test16001
from Selenium学习 import test16002

'''
    有多个测试文件时:
    1. suite = unittest.TestLoader().discover("指定搜索的目录文件","指定字母开头模块文件")
    2. suite = unittest.defaultTestLoader.discover("指定搜索的目录文件","指定字母开头模块文件") 【推荐】

    # unittest.defaultTestLoader, 使用discover()去加载测试用例
    # 找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到文件名才能被加载
    
    discover(start_dir, pattern=‘test*.py’, top_level_dir=None)
        start_dir:要测试的模块名 或 测试用例目录
        pattern=“test*.py”: 表示用例文件名的匹配原则。 此处匹配文件名以“test”开头的“.py”类型的文件,*表示任意字符
        top_level_dir=None:测试模块的顶层目录, 如果没有顶层目录, 默认为None

'''

def creatSuit():
    # 把一个文件夹下面所有的测试脚本中的测试用例放入测试套件
    # discover 正则表达式匹配
    discover = unittest.defaultTestLoader.discover("../Selenium学习", pattern="test1600*.py", top_level_dir=None)
    return discover

if __name__ == "__main__":
    suit = creatSuit()
    runner = unittest.TextTestRunner()
    runner.run(suit)

4. TestRunner

TextTestRunner 测试用例运行器:
run() 方法是测试用例的执行, 入参为suite测试套件

import unittest


class Test(unittest.TestCase):

    def test_add01(self):
        print(1+1)

    def test_add02(self):
        print(2+2)


if __name__ == '__main__':
    # 实例化TestSuite
    suite = unittest.TestSuite()
    # 添加测试用例
    suite.addTest(Test("test_add01"))
    suite.addTest(Test("test_add02"))
    # 实例化TextTestRunner
    runner = unittest.TextTestRunner()
    # 传入suite并执行测试用例
    runner.run(suite)  

3. 忽略测试用例的执行

在遇到不想执行的测试用例时,可以使用skip方法

  1. unittest.skip(reason) :无条件跳过用例, reason是说明原因
  2. unittest.skipIf(condition, reason):condition为true时跳过用例
  3. unittest.skipUnless(condition, reason):condition为False的时候跳过
import unittest


class Test(unittest.TestCase):

    @classmethod
    def setUpClass(self):
        super().setUpClass()
        print("测试前的操作")

    @classmethod
    def tearDownClass(self):
        super().tearDownClass()
        print("测试后的操作")

    @unittest.skip("无条件跳过")
    def test_01(self):
        self.assertIn("a","aa")

    @unittest.skipIf(1 < 2, "1<2 为真,跳过")
    def test_02(self):
        self.assertIn("a","aa")

    @unittest.skipUnless(1 > 2, "1>2为假,跳过")
    def test_03(self):
        self.assertNotIn("a","bb")

    def test_04(self):
        self.assertNotIn("a","bb")

4. 断言

1. 什么是断言

让程序代替人工自动的判断预期结果和实际结果是否相符

断言的结果:
①. True,用例通过
②. False,代码抛出异常,用例不通过
③. 在unittest中使用断言,需要通过 self.断言 方法

2. 为什么要断言

自动化脚本执行时都是无人值守,需要通过断言来判断自动化脚本的执行是否通过

3. 常用的断言

方法检查
assertEqual(a, b)a == b
assertNotEqual(a, b)a != b
assertTrue(x)x的布尔值为真
assertFalse(x)x的布尔值为假
assertIn(a, b)a in b
assertNotIn(a, b)a not in b
import unittest


class Test(unittest.TestCase):
    def setUp(self):
        super().setUp()
        print("测试开始")

    def test_01(self):
        print("1 == 1")
        self.assertEqual(1, 1)  # 成功

    def test_02(self):
        print("1 != 2")
        self.assertNotEqual(1, 2)  # 成功

    def test_03(self):
        print("1 < 2")
        self.assertTrue(1 < 2)  # 成功

    def test_04(self):
        print("1 > 2")
        self.assertFalse(1 > 2)  # 成功

    def test_05(self):
        print("a in aa")
        self.assertIn("a", "aa")  # 成功

    def test_06(self):
        print("a not in bb")
        self.assertNotIn("a", "bb")  # 成功

    def tearDown(self) -> None:
        super().tearDown()
        print("测试结束")

5. HTML 报告生成

  1. 首先要在 Python\Python310\Lib 下放入 HTMLTestRunner.py
  2. 报告生成步骤:
    解决 HTML 文件存放问题
    解决 HTML 命名问题
    生成报告
import HTMLTestRunner
import os
import sys
import time
import unittest


def createsuite():
    discovers = unittest.defaultTestLoader.discover("../Selenium学习", pattern="demo00*.py", top_level_dir=None)
    print(discovers)
    return discovers


if __name__=="__main__":
    # 文件夹的创建
    curpath = sys.path[0]
    print(sys.path)
    print(sys.path[0])

    # 创建文件夹,当前路径下创建
    if not os.path.exists(curpath+'/resultReport'):
        os.makedirs(curpath+'/resultReport')

    # 文件夹的命名,不能让名称重复
    # 时间  时分秒 
    now = time.strftime("%Y-%m-%d-%H %M %S", time.localtime(time.time()))
    print(now)
    print(time.time())
    print(time.localtime(time.time()))

    # 文件名
    filename = curpath + '/resultReport/'+ now + 'resultReport.html'

    with open(filename, 'wb') as fp:

        runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title="测试报告",
                                               description="用例执行情况", verbosity=2)
        suite = createsuite()
        runner.run(suite)

请添加图片描述

6. 数据驱动(unittest ddt)

用数据驱动测试用例不断的执行,直至所有测试数据执行完

数据驱动的特点

  1. 代码和数据分离,避免代码冗余
  2. 不需要重复的代码逻辑
  • 首先需要在 pycharm 底部打开 Terminal ,输入 pip install ddt
  • 使用数据驱动,要在class前加上修饰器 @ddt
from selenium.webdriver.common.by import By
from selenium import webdriver
from ddt import ddt, unpack, data, file_data
import unittest
import time
import json


'''
# 使用数据驱动,要在class前加上修饰器 @ddt
# @ddt
class TestBaidu(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.get('https://www.baidu.com/')

    def tearDown(self):
        time.sleep(3)
        self.driver.quit()

    # 单一参数
    @data('杨幂', '杨洋')
    def test_01(self, name):
        self.driver.find_element(By.ID,'kw').send_keys(name)
        self.driver.find_element(By.ID,'su').click()
'''

'''
# 使用数据驱动,要在class前加上修饰器 @ddt
@ddt
class TestDemo(unittest.TestCase):

    # 多参数数据驱动
    @data(['张三', '94'],['李四','82'],['王五','86'])
    # unpack 是进行拆包,将列表中的两个数据分别传入对应的变量中
    @unpack
    def test_2(self, name, score):
        print( name + ' : '+ score)
'''



'''
# split():分割,返回列表
# strip():去掉两边的字符或者字符串,默认删除空白符(包括'\n', '\r',  '\t',  ' ')
def readTxt():
    rows = []
    with open('C:\\Users\\Jchent\\Desktop\\test1.txt', 'rt', encoding='utf-8') as f:
        for line in f.readlines():
            rows.append(line.strip('\n').split(','))
    return rows

class testDemo(unittest.TestCase):
    def test_01(self):
        txt = readTxt()
        print(txt)
'''


# 用json多个参数读取
def reads_score():
    with open('C:\\Users\\Jchent\\Desktop\\test_Data.json', encoding='utf-8') as f:
        result = json.load(f)  # 列表
        return result

@ddt
class TestDemo(unittest.TestCase):
    # 多参数数据驱动
    @data(*reads_score())
    # unpack 是进行拆包,将列表中的两个数据分别传入对应的变量中
    @unpack
    def test_2(self, name, score):
        print(name + ' : '+ score)


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

7. 截图操作

用例不可能每一次运行都成功,肯定运行时候有不成功的时候。如果可以捕捉到错误,并且把错误截图保存,将会给我们错误定位带来方便

截图方法:driver.get_screenshot_as_file()

# 截图函数
    def errorScreenShot(self, driver, file_name):  # 参数:驱动,截图名字
        if not os.path.exists("./demoImage"):
            os.makedirs("./demoImage")
        now = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time()))
        driver.get_screenshot_as_file("./demoImage/" + now + "-" + file_name)
        time.sleep(3)
from selenium import webdriver
from selenium.webdriver.common.by import By
import unittest
import time
import os


class Baidu1(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(30)
        self.base_url = "http://www.baidu.com/"
        self.verificationErrors = []
        self.accept_next_alert = True

    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors)

    def test_baidu(self):
        driver = self.driver
        driver.get(self.base_url)
        driver.maximize_window()
        driver.find_element(By.ID, "kw").send_keys("极限挑战")
        driver.find_element(By.ID, "su").click()
        time.sleep(3)
        print(driver.title)

        try:
            self.assertEqual(driver.title, "百度一下,你就知道", msg="不相等")
        except:
            self.errorScreenShot(driver, "error.png")  # 出现异常就调用截图函数进行截图
        time.sleep(5)

    # 截图函数
    def errorScreenShot(self, driver, file_name):  # 参数:驱动,截图名字
        if not os.path.exists("./demoImage"):
            os.makedirs("./demoImage")
        now = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time()))
        driver.get_screenshot_as_file("./demoImage/" + now + "-" + file_name)
        time.sleep(3)


if __name__ == "__main__":
    unittest.main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值