IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 自动化测试框架 unittest 基础学习 -> 正文阅读

[开发测试]自动化测试框架 unittest 基础学习

自动化测试框架Unittest

unittest框架 这个框架是python的黑盒测试的框架
注意区分一个容易混淆的。Junit是java白盒测试的框架。
unittest是如何体现出黑盒测试的?它是web界面的功能测试的框架
unittest 单元测试提供了创建测试用例,测试套件以及批量执行的方案, unittest 在安装pyhton 以后就直接自带了,直接import unittest 就可以使用。
各个组件关系图

在这里插入图片描述

Test Fixture (测试固件)

里面有很多固定的方法,初始化和清理测试环境,比如创建临时的数据库,文件和目录等。其中setup() 和 setDown()是最常用的方法
任何继承自unnitTest类里面的而固定方法
setUp():初始化方法
tearDwon():清理工作
每执行一个test_方法 都会对应重新执行setUp和tearDown()

TestCase (测试用例)

单元测试用例,TestCase是编写单元测试用例最常用的类
每一个test_ 开头的方法 都可以叫做测试用例
运行tetst_ 开头所在的类的时候都会默认执行,但是这个类里面的其他方法需要显示的手动调用执行

代码演示测试固件和测试用例


from selenium import webdriver
import unittest
import time
import os

# python 里面的继承语法 继承unittest框架里面的TestCase类
class TestCase1(unittest.TestCase):
    # 在测试方法之前进行初始化 这是一个测试固件
    # self 代表的是TestCase类的实例
    # setUp 和 tearDown 方法都是默认执行的 是测试固件
    def setUp(self):
        # 要使driver变量成为全局变量 必须要在前面加上self
        self.driver = webdriver.Chrome()
        self.url = "https://www.baidu.com/"
        self.driver.get(self.url)
        self.driver.maximize_window()
        time.sleep(2)

    #  在测试完成后 清理内容
    def tearDown(self):
        self.driver.quit()

    #  测试方法1
    # 关于方法的名字 如果方法的名字是以test_ 开头的 就会默认去运行
    # 在所有的test方法开始以前都会去默认执行所有的测试固件
    def test_baidu1(self):
            self.driver.find_element_by_id("kw").send_keys("张杰巡回演唱会")
            self.driver.find_element_by_id("su").click()
            time.sleep(2)

  
    #  测试方法二
    def test_baidu2(self):
        self.driver.find_element_by_link_text("新闻").click()
        time.sleep(3)
 
    # python的入口方法
    if __name__ == "__main__":
        # 参数?? 默认是0 表示每一个测试方法执行的情况
        unittest.main(verbosity=1)

关于verbosity 的参数解释

可以增加verbosity参数,例如unittest.main(verbosity=2) 在主函数中,直接调用main()
,在main中加入verbosity=2 ,这样测试的结果就会显示的更加详细。 这里的verbosity 是一个选项,表示测试结果的信息复杂度,有三个值
0 ( 静默模式): 你只能获得总的测试用例数和总的结果比如总共100个失败,20 成功80
1 ( 默认模式): 非常类似静默模式只是在每个成功的用例前面有个“ 对勾 ” 每个失败的用例前面有个“F”
2 ( 详细模式):测试结果会显示每个测试用例的所有相关的信息

Test Suite测试套件

完整的单元测试很少只执行一个测试用例,开发人员通常都需要编写多个测试用例才能对某一软件功能进行比较完整的测试,这些相关的测试用例称为一个测试用例集,在unittest中是用TestSuite 类来表示的假设我们已经编写了testbaidu1.py,testbaidu2.py两个文件,那么我们怎么同时执行这两个文件呢?需要使用测试套件

测试套件就是存放测试用例(测试方法)的一个容器,是单元测试用例的结合 其中最常用的类就是TsetSuite类

1.TestSuite()类

该类代表单个测试用例和测试套件的集合。它提供了运行测试所需的接口以使其可以像其他测试一样运行。TestSuite实例和遍历套件相同,单独运行每个测试用例。

TestSuite的行为和TestCase非常相似,但它并未实际执行测试,而是用于将测试用例聚合到一起,下面的2个方法用于向TestSuite实例中添加测试用例:

1.1 addTest():添加测试用例到TestCase或TestSuite套件中; 可以一次存放一个测试脚本里一个类的一个方法(提供方法名)

# 测试套件
# 使用addTest方法 来添加类里面的方法
import unittest
# 虽然是在一个文件夹下,但是python不像java 可以找到
from bite.src.unittest import test001
from bite.src.unittest import test002

def createSuite():
    # 这个语句实例化了一个测试套件
    # 注意这个语句需要加()
    suite = unittest.TestSuite()

    # 将test001 TestCase类里面的两个方法加入了测试套件
    # 使用类的addTest方法将测试用例加入测试套件

    suite.addTest(test001.TestCase1("test_baidu1"))
    suite.addTest(test001.TestCase1("test_baidu2"))
    suite.addTest(test002.TestCase2("test_baidu1"))
    suite.addTest(test002.TestCase2("test_baidu2"))
    return suite

缺点:一次只能添加一个类里的一个方法,方法太多的时候,比较麻烦

1.2 addTests():将迭代TestCase和TestSuite实例中的所有测试用例添加到此测试组件,相当于调用addTest()然后添加的每个元素。 可以和Testloader结合使用

1.3 makeSuite() :unittest 框架中提供了makeSuite() 的方法。
可以一次性的添加测试脚本的一个类的所有方法(test_ 开头的方法),比起addTest更方便,但是本质上还是在调用addTest方法

  • 传入的参数是测试类的名称即可
def createSuite():
    # 这个语句是在做什么 这里是实例化了一个测试套件
    # 注意这个语句需要加()
    suite = unittest.TestSuite()
    # 但是如果一个类里面有很多需要添加的测试方法,这样一个个的添加很麻烦
    # 所以使用makesuit

    suite.addTest(unittest.makeSuite(test001.TestCase1))
    suite.addTest(unittest.makeSuite(test002.TestCase2))
    return suite

 suite.addTest(unittest.makeSuite(test001.TestCase1))

    # 上面的和下面的效果等价 makeSuite 就是在创建一个测试套件
    # suite1 = unittest.makeSuite(test001.TestCase1)
    # suite.addTest(suite1)

2.TestLoader()类

  • TestLoader类被用来创建类和模块的测试套件

  • 通常不需要创建该类的实例。
    unittest框架提供了一个可以共享的实例unittest.defaultTestLoader。
    可以一次性存放一个测试脚本的一个类的所有方法,但是用法不太一样,是把类里面的方法先添加到一个小的测试套件

  • 一般的情况下,使TestLoader().loadTestsFromTestCase(TestClass)
    来加载测试类。

def createSuite():

    suite = unittest.TestSuite()

    # 使用Testloader
    # 先把一个类里面的添加到一个小的测试套件里面去
    suite1 = unittest.TestLoader().loadTestsFromTestCase(test001.TestCase1)
    suite2 = unittest.TestLoader().loadTestsFromTestCase(test002.TestCase2)
    # 然后将这两个一起 在加入大的测试套件里面
    # [] 是一个列表
    suite.addTests([suite1,suite2])

3. discover()方法

可以把指定文件夹下,所有以某种格式命名的测试脚本类中所有以test_开头的方法加入到测试套件中

参数解析
discover(start_dir, pattern =‘test *.py’, top_level_dir = None )

start_dir:要测试的模块名或测试用例目录;

pattern=‘test*.py’:表示用例文件名的匹配原则,下面的例子中匹配文件名为以“test”开头的“.py”文件,星号“*”表示任意多个字符;

top_level_dir=None:测试模块的顶层目录,如果没有顶层目录,默认为None;

该方法通过从指定的开始目录递归到子目录中查找所有测试模块,并返回包含它们的TestSuite对象,只有与模式匹配测试文件和可导入的模块名称才会被加载。

所有测试模块必须可以从项目的顶层导入,如果起始目录不是顶层目录,则顶层目录必须单独指定。

  • 如果一个测试文件的名称符合pattern,将检查该文件是否包含 load_tests() 函数,如果 load_tests() 函数存在,则由该函数负责加载本文件中的测试用例。

  • 如果不存在,就会执行loadTestsFromModule(),查找该文件中派生自TestCase 的类包含的 test 开头的方法

def createSuite():
	 discover = unittest.defaultTestLoader.discover('../unittest',pattern= 'test00*.py',top_level_dir= None)

    return discover

TestRunnner

执行单元测试
在某一个单元测试用鼠标右击只可以启动一个测试用例,如果想同时启动该类的所有测试方法,需要通过python的入口函数。
如果只是单纯的执行测试用例,unittest.main()来启动单元测试的测试模块

  # python的入口方法
    if __name__ == "__main__":
        # 参数?? 默认是0 表示每一个测试方法执行的情况
        unittest.main(verbosity=1)

runner 方法

unittest.TextTestRunnner().run () 执行测试套件

如果是将一个个的测试文件,添加到测试套件之后,就需要运行这个测试用例

if __name__ == "__main__":

    suite = createSuite()
    # 这里的默认参数是0 但是当是1 或者是 2 的时候,执行出来的信息会比较详细
    # TestRunner 用来执行测试
    # 2是最详细的
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

测试方法/用例执行的顺序

unittest 框架家长测试用例的顺序是根据Ascii码的顺序,数字与字母的顺序
0~9(48- 57) 、 A ~ Z(65- 90) 、 a ~ z(97-122)

忽略测试用例的执行

@unittest.skip(“skipping”)
只需要在需要忽略执行的测试方法前面加上注解即可。

unittest断言

序号断言方法断言描述
1assertEqual(arg1, arg2, msg=None)验证arg1=arg2,不等则fail
2assertNotEqual(arg1, arg2, msg=None)验证arg1 != arg2, 相等则fail
3assertTrue(expr, msg=None)验证expr是true,如果为false,则fail
4assertFalse(expr,msg=None)验证expr是false,如果为true,则fail
5assertIs(arg1, arg2, msg=None)验证arg1、arg2是同一个对象,不是则fail
6assertIsNot(arg1, arg2, msg=None)验证arg1、arg2不是同一个对象,是则fail
7assertIsNone(expr, msg=None)验证expr是None,不是则fail
8assertIsNotNone(expr, msg=None)验证expr是None,不是则fail
9assertln (arg1,arg2,msg = None)验证arg1 是arg2的字串,不是则fail
10assertlNotn (arg1,arg2,msg = None)验证arg1 不是arg2的字串,是则fail
11assertIsInstance(obj,cls,msg = None)验证obj是cls的实例,不是则fail
12assertNotIsInstance(obj,cls,msg = None)验证obj不是cls的实例,是则fail
from selenium import webdriver
import time
import unittest
# assert 的学习
class assert1 (unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        url = "https://www.baidu.com/"
        self.driver.get(url)

    def test_asset1(self):
        # 注意这句话
        driver = self.driver
        driver.find_element_by_id("kw").send_keys("张杰")
        driver.find_element_by_id("su").click()
        # 如果不进行sleep 那么driver 的title 是百度一下,你就知道
        time.sleep(2)
        print(driver.title)
        self.assertEqual(driver.title,"张杰_百度搜索",msg="not equal!")
        self.assertNotEqual(driver.title,"张杰_百度搜索",msg="equal!")
        self.assertTrue(1 == 2,msg="False")

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

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

TestReport

生成测试报告 html报告
有的时候,我们需要执行很多的测试用例,不可能一个个去看是否执行成功,那么久可以使用生成html报告的形式来做。

# 生产测试报告
import unittest
import HTMLTestRunner
import os, sys
import time


def createSuite():

    # 使用discover 测试套件
    discover = unittest.defaultTestLoader.discover("../unittest", pattern='test00*.py', top_level_dir=None)
    return discover


if __name__ == '__main__':
    # 获取当前文件所在的文件路径
    # 方法一是如下所示 方法二是  os.path.exists("./errorImage"):
    # 都可以达到判断当前的目录下是否有这个文件夹
    curpath = sys.path[0]
    print(curpath)
    # 查看当前的路径下有没有这个文件夹
    if not os.path.exists(curpath+"/resultReport"):
        os.mkdir(curpath + "/resultReport")
    # time.time() 获取linux的时间戳
    # time.localtime 转换为当地时间(所在地区的时间)time.time()))
    # % % 中英文的百分号看不出来 贼
    # strftime 用于格式化时间函数
    now = time.strftime("%Y-%m-%d %H %M %S",time.localtime(time.time()))
    # 用时间来命名文件 不会出现名字冲突
    filename = curpath + "/resultReport/" + now + "-" + "resultReport.html"

    # 以什么样的权限打开文件
    # 创建文件的方式一 使用open函数
    # 是python用来打开本地文件的,他会在使用完毕后,自动关闭文件,无需手动书写
    # 还有一种是使用webdriver 下的 get_screenshot_as_file  创建一个错误截图的文件
    # with 是python的一个关键字
    # w 是写入 b 是指按照字节的方式
    with open(filename, "wb") as fp:
        # u 让输入以unicode编码的方式
        runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u"测试报告",
                                               description=u"测试用例报告", verbosity=2)
        suite = createSuite()
        runner.run(suite)

    # 对下面这些启动流程不太熟悉
    suite =  createSuite()
    run = unittest.TextTestRunner(verbosity= 0)
    run.run(suite)

报告界面展示
在这里插入图片描述

异常捕获和错误截图

异常捕获的语法和java的类似

 # 处理异常
    # 其他的方法如果没有以test_ 开头 就会需要显示的调用才可以
    def is_alert_exist(self):
        try:
            self.driver.switch_to.alert
        except NoAlertPresentException as e:
            return False
        return True

如果遇到一些异常的情况,需要把错误信息或者是截图保留下来。



from selenium import webdriver
import unittest
import time
import os,sys
from selenium.common.exceptions import NoAlertPresentException

# python 里面的继承语法 继承unittest框架里面的TestCase类
class TestCase1(unittest.TestCase):
    # 在测试方法之前进行初始化 这是一个测试固件
    # self 代表的是TestCase类的实例
    # setUp 和 tearDown 方法都是默认执行的 是测试固件
    def setUp(self):
        # 要使driver变量成为全局变量 必须要在前面加上self
        self.driver = webdriver.Chrome()
        self.url = "https://www.baidu.com/"
        self.driver.get(self.url)
        self.driver.maximize_window()
        time.sleep(2)

    #  在测试完成后 清理内容
    def tearDown(self):
        self.driver.quit()


    def test_baidu1(self):
        # 判断有没有打开百度页面
        try:
            print(self.driver.title)
            self.assertEqual(self.driver.title, "百,你就知道", msg="判断失败,没有打开页面")
        except:
            # 实际传递参数的时候 可以发现并没有传递self 这个是一个全局变量
            self.save_error_image(self.driver, "baidu.png")


    def save_error_image(self, driver, name):
        # self 是类的实例
        # 创建文件夹
        if not os.path.exists("./errorImage"):
            os.mkdir("./errorImage")
        now = time.strftime("%Y-%m-%d #H %M %S", time.localtime(time.time()))
        # 创建文件 这里如果要用self 必须传进来
        self.driver.get_screenshot_as_file('./errorImage/' + now + "-" + name)

        time.sleep(3)




    # python的入口方法
    if __name__ == "__main__":
        # 参数?? 默认是0 表示每一个测试方法执行的情况
        unittest.main(verbosity=1)


由于是不相等的,所以在当前文件的目录下,可以看到一个errorImage 目录下存放的一些错误的截图。

数据驱动

之前我们的case都是数据和代码在一起编写。考虑如下场景:
需要多次执行一个案例,比如baidu搜索,分别输入中文、英文、数字等进行搜索,这样需要分别写多个Test,所以可以使用把要测试的数据写在一个测试文件里面,读取这个数据文件。

python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT
来完成。

ddt.data

装饰测试方法,参数是一系列的值。

同时执行多个测试数据

# -*- coding: utf-8 -*-
from selenium import webdriver
import os, time
import unittest
from ddt import ddt, unpack, data, file_data
import sys, csv

@ddt
class ddt(unittest.TestCase):
    # 测试固件
    def setUp(self):
        self.driver = webdriver.Chrome()
        url = "https://www.baidu.com/"
        self.driver.get(url)

    # 运行这下面的例子的时候 并没有写main函数


    # 在方法的前面加上注解 同时设置参数
    # 方法的传入设置多加一个参数
    # 就可以达到 依次把参数传入的效果
    # 相当于执行了三个测试用例
    # 降低了代码冗余度

    @data("xqr", "张杰zj", "阿里巴巴") # 注意这个位置必须是紧挨着 def 的在它的上一行
    def test_baidu(self, value1):
        driver = self.driver
        driver.find_element_by_id("kw").send_keys(value1)
        driver.find_element_by_id("su").click()
        time.sleep(2)



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

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

ddt.file_data

装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。
注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件处理。
如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。
如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。

如果测试的数据很多,那么可以使用读取jason 文件的方式 setUp tearDwon main 都不变化,只需进程如下的写法即可。

  @file_data("baidu_test.json")
    def test_baidu(self, value1):
        driver = self.driver
        driver.find_element_by_id("kw").send_keys(value1)
        driver.find_element_by_id("su").click()
        time.sleep(2)

baidu_test.json
在这里插入图片描述

ddt.unpack:

传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多
个参数上。字典也可以这样处理。

读取list

 @data(["谢娜", "谢娜_百度搜索"], ["张杰", "张杰_百度搜索"], ["李宁", "李宁_百度搜索"])
    @unpack
    def test_baidu1(self, value1, value2):
        driver = self.driver
        driver.find_element_by_id("kw").send_keys(value1)
        driver.find_element_by_id("su").click()
        time.sleep(3)
        print(driver.title)
        self.assertEqual(driver.title, value2, msg="Not Equal")

读取文件

# -*- coding: utf-8 -*-
from selenium import webdriver
import os, time
import unittest
from ddt import ddt, unpack, data, file_data
import sys, csv


# 引入ddt 需要的包


# 定义打开Csv文件的方法
def getCsv(file_name):
    # 数组里面存放读取出来的数据
    rows = []
    path = sys.path[0]  # 获取当前类所在的根路径 也就是ddt这个目录
    # 以只读的方式打开文本文件
    # 同时应该注意指定的参数
    with open(path + "\\" + file_name, 'rt', encoding="UTF-8") as f:
        readers = csv.reader(f, delimiter=',', quotechar='|')  # quptechar 回车
        next(readers, None)  # 一行一行的读取 读取的的一行一行就存放在readers里面
        for row in readers:
            temprows = []
            for i in row:
                temprows.append(i)
            rows.append(temprows)

        # ([周迅,周迅_百度搜索],[张国荣,张国荣_百度搜索],[张一山,张一山_百度搜索])
        return rows


# 加上ddt注解
@ddt
class ddt(unittest.TestCase):
    # 测试固件
    def setUp(self):
        self.driver = webdriver.Chrome()
        url = "https://www.baidu.com/"
        self.driver.get(url)

    # 放入txt 文件中读取
    # 并且由于py是解释型语言 那么必须把getCsv的方法放在最前面
    # 传输文件名字的时候必须是全路径
    # 单星号(*):*agrs 将所有参数以元组(tuple)的形式导入: 单星号的另一个用法是解压参数列表:
    @data(*getCsv("test_baidu_data.txt"))
    @unpack  # 打开 可以实现数据的一一对应
    def test_baidu(self, value1, value2):
        driver = self.driver
        driver.find_element_by_id("kw").send_keys(value1)
        driver.find_element_by_id("su").click()
        time.sleep(3)
        print(driver.title)
        self.assertEqual(driver.title, value2, msg="Not Equal")

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


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

test_baidu_data.txt
(这个data 写不写都行 但是具体原因没查到!) 没什么影响,不太知道为什么。
在这里插入图片描述

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-07-07 00:07:16  更:2021-07-07 00:07:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 17:04:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码