文章目录
target
- UnitTest 框架
- fixture
- 断言
- HTML测试报告
一、UnitTest 框架
单元测试 - 白盒测试
就是对程序源代码进行测试
framework 框架:为解决一类事情的功能集合
为什么使用UnitTest框架
- 能够组织多个用例去执行
- 提供丰富的断言方法
- 能够生成测试报告
UnitTest核心要素
1. TestCase 测试用例
2. TestSuite 测试套件
3. TestRunner 测试执行 (TextTestRunner 以文本的形式运行测试用例)
4. TestLoader 批量执行测试用例,搜索指定文件夹里的模块,方法,类等等,推荐使用)
5. Fixture 固定装置:两个固定的函数,一个初始化的时候使用,一个结束的时候使用
测试用例
使用;
- 导包: import unittest
- 定义测试类 :新建测试类必须继承
unittest.TestCase
- 定义测试方法:测试方法名称命名必须以
test
开头
执行测试用例
方式一:
使用pycharm在代码上点击鼠标右键,选择使用UnitTest运行
方式二:
调用 unittest.main() 来运行
# TestCase 使用
# case:求和测试函数
import unittest
# 求和函数
def add(x,y):
return x+y
# 定义测试类 并 继承 大驼峰是类
class Test01(unittest.TestCase):
# 定义测试方法: 注意:以test 开头
def test_add(self):
# 调用 要测试的函数
result = add(1,1)
print("结果为:",result)
if __name__ == '__main__':
unittest.main()
class Test01:
def test001(self)
print("test001被执行"):
if __name__ == '__main__':
print("__name__ 的值为:",__name__)
""""
__name__: 为python中内置变量
值:
1.如果当前运行的模块是主模块,那么 __name__ 的值为:__main__
2.如果当前运行的模块不是主模块,那么 __name__ 的值为:模块名称
"""
TestCase:
说明:测试用例
步骤:
1. 导包 import unittest
2. 新建测试类并继承 unittest.TestCase
3. 测试方法必须以test字母开头
运行:
1. 运行测试类所有的测试方法,光标定位到类当前行右键运行
2. 运行单个测试方法:光标放到测试方法当前行。
测试套件 + 执行 —— testSuit TextTestRunner
TestSuite:
说明:测试套件
步骤:
1. 导包
2. 获取测试套件对象 suite = unittest.TestSuite()
3. 调用addTest()方法 添加测试用例
添加测试用例方法:
1. suite.addTest(类名("方法名称")) # 添加指定类中指定的测试方法
2. suite.addTest(unittest.makeSuite(类名)) # 添加指定类中所有已test开头的方法
TextTestRunner:
说明:执行测试套件方法
步骤:
1. 导包
2. 实例化后去执行套件对象 runner = unittest.TextTestRunner()
3. 调用run方法去执行 runner.run(suite)
import unittest
class Test02(unittest.TestCase):
def test001(self):
print("test001被执行")
def test002(self):
print("test002被执行")
def test003(self):
print("test003被执行")
def test004(self):
print("test004被执行")
if __name__ == '__main__':
print("__name__的值:", __name__)
"""
__name__: 为python中内置变量
值:
1. 如果当前运行的模块为当前模块,那么__name__的值为:__main__
2. 如果当前运行的模块不是主模块,那么__name__的值为:模块名称
"""
TestLoader
核心代码就这三句
import unittest
suite = unittest.defaultTestLoader.discover("指定搜索的目录文件","指定字母开头模块文件")
unittest.TextTestRunner().run(suite)
说明:
1. 将符合条件的测试方法添加到测试套件中
2. 搜索指定目录文件下指定字母开头的模块文件下test开始的方法,并将这些方法添加到测试套件中,最后返回测试套件
操作:
1. 导包
import unittest
2. 调用TestLoader()
写法1. suite = unittest.TestLoader().discover("指定搜索的目录文件","指定字母开头模块文件")
写法2. suite = unittest.defaultTestLoader.discover("指定搜索的目录文件","指定字母开头模块文件") 【推荐】
注意:如果使用写法1,TestLoader()必须有括号。
3. 执行测试套件
unittest.TextTestRunner().run(suite)
"""
目标:演示TestLoader()类的用法
作用:搜索指定目录下指定开头的py文件,在Py文件中搜索test开头测试方法,并且将这些方法添加到测试套件中,最后返回测试套件
需求:
运行 cases目录下test01.py~test05.py文件
操作:
1. 导包 unittest
2. 调用TestLoader类下discaver方法
3. 执行测试用例
"""
# 导包
import unittest
# 调用方法
# suite = unittest.TestLoader().discover("../cases")
# 扩展 指定 tpshop开头的模块
# suite = unittest.TestLoader().discover("../cases", pattern="tpshop*.py")
# 扩展 使用 推荐使用
suite = unittest.defaultTestLoader.discover("../cases")
# 执行 套件 方法 TextTestRunner
unittest.TextTestRunner().run(suite)
TestSuite与TestLoader区别:
共同点:都是测试套件
不同点:实现方式不同
TestSuite: 要么添加指定的测试类中所有test开头的方法,要么添加指定测试类中指定某个test开头的方法
TestLoader: 搜索指定目录下指定字母开头的模块文件中以test字母开头的方法并将这些方法添加到测试套件中,最后返回测试套件
二、fixture —— (setUp、tearDown) 很重要
就是两个函数,初始化函数,结束函数
说明:装置函数(setUp、tearDown)
级别:
1). 函数级别 def setUp() / def tearDown()
特性:几个测试函数,执行几次。每个测试函数执行之前都会执行 setUp,执行之后都会执行tearDwon
2). 类级别 def setUpClass() / def tearDownClass()
特性:测试类运行之前运行一次setUpClass 类运行之后运行一次tearDownClass
注意:类方法必须使用 @classmethod修饰
3). 模块级别:def setUpModule() / def tearDownModule()
特殊:模块运行之前执行一次setUpModule ,运行之后运行一次tearDownModule
提示:
无论使用函数级别还是类级别,最后常用场景为:
初始化:
1. 获取浏览器实例化对象
2. 最大化浏览器
3. 隐式等待
结束:
关闭浏览器驱动对象
def setUp()是TestCase 的子类,TestCase是父类,要继承class Test04(unittest.TestCase):
使用TestCase就要导包 import unittest
"""
目标:unittest 中的fixture用法
fixture其实就是两个函数,这个函数可以一起使用,也可以单独使用
1. 初始化函数:def setUp()
2. 结束函数: def tearDown()
fixture级别:
1. 函数级别 [常用]:有几个测试函数,setup 就运行几次。 好处:每次初始化和结束都不会出错,方便。 缺点:启动慢,每次都要调用setup
2. 类级别 [更常用]:在测试类运行之前,首先会执行setup 里面的方法,然后在类执行之后后执行teardown 里面的方法
3. 模块级别:模块运行之前执行一次setUpModule ,运行之后运行一次tearDownModule
"""
import unittest
def setUpModule():
print("setUpModule")
def tearDownModule():
print("tearDownModule")
class Test03(unittest.TestCase): # 这是继承
@classmethod
def setUpClass(cls):
print("setUpClass被执行")
@classmethod
def tearDownClass(cls):
print("tearDownClass被执行")
def setUp(self):
print("setUp被执行")
def tearDown(self):
print("tearDown被执行")
def test01(self):
print("test01被执行")
def test02(self):
print("test02被执行")
class Test04(unittest.TestCase):
# 类方法 ,用的多
@classmethod # 修饰类方法,类方法必须修饰
def setUpClass(cls):
print("setUpClass被执行")
@classmethod
def tearDownClass(cls):
print("tearDownClass被执行")
def setUp(self):
print("setUp被执行")
def tearDown(self):
print("tearDown被执行")
def test01(self):
print("test01被执行")
def test02(self):
print("test02被执行")
三、断言 —— assert 就是测试
1 什么是断言?
让程序代替人为判断执行结果是否与预期结果相等的过程
2 为什么要断言?
自动化脚本执行时都是无人值守,需要通过断言来判断自动化脚本的执行是否通过
注:自动化脚本不写断言,相当于没有执行测试一个效果。
3 常用断言
1. self.assertEqual(ex1, ex2) #判断ex1 是否相等ex2
2. self.assertIn(ex1 ,ex2) # ex2是否包含ex1 注意:所谓的包含不能跳字符
3. self.assertTrue(ex) # 判断ex是否为True
4 断言练习
目标:tpshop登录
方法:
def setUp():
# 获取driver
# 打开url
# 最大化浏览器
# 隐式等待
def tearDown():
# 关闭浏览器驱动
def test_login_code_null():
# 根据业务流程编写
# ...
# 失败截图
5 扩展
断言两种实现方式
方式1:使用unittest框架中断言,详情请参考以上相关知识点。
方式2:使用python自带断言
1. assert str1 == str2 # 判断str1 是否与str2相等
2. assert str1 in str2 # 判断str2 是否包含str1
3. assert True/1 # 判断是否为True
3 常用断言
self.assertTrue(ex) # 判断ex是否为True
self.assertEqual(ex1, ex2) #判断ex1 是否相等ex2
self.assertIn(ex1 ,ex2 # ex2是否包含ex1 注意:所谓的包含不能跳字符
import unittest
"""
目标:unittest常用断言
1. assertTrue : 如果结果为True通过,否则失败
"""
class Test02(unittest.TestCase): #继承case
def test001(self): # 子类调用父类方法用 self
# 断言是否为True
# flag = True
# flag = False
# self.assertTrue(flag)
# self.assertFalse(flag)
# 判断两个字符串是否相等
# self.assertEqual("你好,斑马!", "你好,奥码!")
# self.assertEqual("你好,斑马!", "你好,斑马!")
# 判断后边的字符串是否包含前边的字符串
# self.assertIn("hello bama", "hello bama,wahaha")
# self.assertIn("hello wahaha", "hello bama,wahaha")
# 判断是否为None
flag = None
# self.assertIsNone(flag)
self.assertIsNotNone(flag)
4 断言练习
"""
目标:断言练习
案例:
输入:正确用户名和密码 验证码为空
断言:提示信息是否为,验证码不能为空!
要求:如果断言出错,截屏保存。
"""
# 导包
import unittest
import time
from time import sleep
from selenium import webdriver
# 定义测试类 并 继承 unittest.TestCase
class TestTphopLogin(unittest.TestCase):
# 定义初始化方法
def setUp(self):
# 获取浏览器驱动对象
# 因为后面teardown要关闭浏览器,和此时的函数不是一个函数,意思就是跨函数了,所以需要self ,这样后面就可以访问到了,可以顺利退出
self.driver = webdriver.Firefox()
# 打开 url
self.driver.get("http://localhost")
# 最大化浏览器
self.driver.maximize_window()
# 隐式等待
self.driver.implicitly_wait(30)
# 定义teardown
def tearDown(self):
# 关闭浏览器驱动
sleep(2)
self.driver.quit()
# 定义登录测试方法 验证码为空
def test_login_code_null(self):
driver = self.driver
# 点击登录连接
driver.find_element_by_partial_link_text("登录").click()
# 输入用户名
driver.find_element_by_css_selector("#username").send_keys("13800001111")
# 输入密码
driver.find_element_by_css_selector("#password").send_keys("123456")
# 输入验证码
driver.find_element_by_css_selector("#verify_code").send_keys("")
# 点击登录
driver.find_element_by_css_selector(".J-login-submit").click()
# 获取登录后提示信息
result = driver.find_element_by_css_selector(".layui-layer-content").text
print("result:", result)
# 定义预期结果
# expect_result = "验证码不能为空!"
# 测试断言错误
expect_result = "验证码不能为空!!"
# try 捕获异常
try:
# 断言
self.assertEqual(result, expect_result)
except AssertionError:
# 截图
driver.get_screenshot_as_file("../image/{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))
# 抛异常
raise
断言两种实现方式 —— unit test 和 python自带的
"""
目标断言扩展:
两种实现方式:
1. 使用unittest框架中的断言
2. 使用python自带断言
"""
# 使用python自带断言 判断两个字符串是否相等
# assert "hello" == "hello"
# 不相等
# assert "hello" == "hello1" "出错啦啦啦!!!这俩不相等"
# 第二个字符串是否包含第一个字符串
# assert "h" in "hello"
# assert "ha" in "hello"
# 判断是否为True
# assert True
# assert False
# 0 为False ; 1为True
# assert 0
# assert 1
四、参数化
4.1 为什么要参数化
解决冗余代码问题;
4.2 什么是参数化
说明:根据需求动态获取参数并引用的过程
4.3 参数化应用场景
场景:解决相同业务逻辑,不同测试数据问题。
4.4 应用:
1. 安装插件
通过命令:
安装:
pip install parameterized
验证:
pip show parameterized
通过pycharm:File-->setting-->Project 工程名称
2. 应用插件
1. 导包 from parameterized import parameterized
2. 修饰测试函数 @parameterized.expand([数据])
数据格式:
1. 单个参数:类型为列表
2. 多个参数:类型为列表嵌套元祖
3. 在测试函数中的参数设置变量引用参数值,注意:变量的数量必须和数据值的个数相同
import unittest
from parameterized import parameterized
def add(x, y):
return x+y
"""
问题:
如果有三组数据需要测试?
[(1,1,2), (1,2,3), (0,3,3)]
"""
def get_data():
return [(1, 2, 3), (3, 0, 3), (2, 1, 3)]
# 定义测试类 并 继承
class Test01(unittest.TestCase):
# 定义测试方法 注意:以test字母开头
@parameterized.expand(get_data())
def test_add(self, a, b, expect):
# 调用 要是的函数
result = add(a, b)
assert result == expect
parameterized 插件应用
import unittest
from parameterized import parameterized
"""
目标:parameterized 插件应用
步骤:
1. 导包 from parameterized import parameterized
2. 修饰测试函数 @parmeterized.expand(列表类型数据)
3. 在测试函数中使用变量接收,传递过来的值。
语法:
1. 单个参数:值为列表
2. 多个参数:值为列表嵌套元组 如:[(1,2,3),(2,3,4)]
"""
# 定义测试类 并 继承
class Test01(unittest.TestCase):
# 单个参数使用方法
# @parameterized.expand(["1", "2", "3"])
# def test_add_one(self, num):
# print("num:", num)
# 多个参数使用方法 写法1
# @parameterized.expand([(1, 2, 3), (3, 0, 3), (2, 1, 3)])
# def test_add_more(self, a, b, result):
# print("{}+{}={}:".format(a, b, result))
# data = [(1, 2, 3), (3, 0, 3), (2, 1, 3)]
# 写法2
# @parameterized.expand(data)
# def test_add_more(self, a, b, result):
# print("{}+{}={}:".format(a, b, result))
# 写法 3 推荐
def get_data(self):
return [(1, 2, 3), (3, 0, 3), (2, 1, 3)]
@parameterized.expand(get_data())
def test_add_more(self, a, b, result):
print("{}+{}={}:".format(a, b, result))
注意;
写法三在写的时候,应该先写把调用的函数写前面,不然执行不了