前言:在接口/UI自动化中,unittest框架或多或少也听到过,流行的框架我们需要重点去学习及掌握,接下来就一起来学习下吧
一、unittest核心组件
1、TestCase:测试用例类,用来定义测试用例函数的
2、TestSuite:测试套件,用来收集测试用例的
3、TestRunner:测试用例运行类,用来执行测试用例的,以测试套件维度去执行
4、TestFixture:测试脚手架,前置条件,后置处理
二、测试流程--unittest你是如何做自动化测试的
1、TestCase:先定义测试用例类,在测试用例类中写测试方法
2、TestSuite:收集你要执行的测试用例类或者文件里面的测试方法,放到测试套件
3、TestRunner:将收集好的测试条件放到测试用例运行器去执行,并收集测试结果
4、TestFixture:做好前置条件和后置处理
三、如何写测试用例(TestCase)
1、导入模块:import unittest,继承TestCase类
2、定义一个测试用例类:类名称必须Test开头
3、定义测试方法:测试方法也必须是test开头,必须写在测试用例类里面
4、框架执行入口:unittest.main(),自动收集当前py文件的测试用例,然后执行
四、测试用例包含哪些东西
1、前置条件(如果没有可以不写):sql语句执行、数据库连接创建、工具类的实例化
2、测试步骤(业务逻辑):数据替换、数据驱动
3、测试结果断言:响应结果断言、数据库数据断言
4、后置处理(如果没有可以不写):数据清理、数据库连接关闭
五、前置后置(TestFixture)
自动化中什么是前置后置?
答:前置:sql语句执行、数据库连接创建、工具类的实例化
后置:数据清理、数据库连接关闭
a) 类级别(以类级别的运行)
一、类级别(以类级别的运行)
前置:测试用例执行之前执行一次(当前测试用例类) ——
@classmethod
def setUpClass(cls) -> None:(固定写法,必须加@classmethod,源码就是加了)
后置:测试用例执行之后执行一次(当前测试用例类)——
@classmethod
def tearDownClass(cls) -> None: (固定写法,必须加@classmethod,源码就是加了)
解释:
类级别的前后置就是,比如你的测试用例有3条,那就每一条用例都会去执行前后置,
执行顺序:前置,第一条用例,第二条用例,后置
a) 示例:类级别
#例子:
class Test_class_setup(unittest.TestCase): #前置后置都需要类继承unittest.TestCase
@classmethod #类级别的前置 必须加@classmethod
def setUpClass(cls) -> None:
print("我是类级别的前置")
@classmethod
def tearDownClass(cls) -> None: #类级别的后置 必须加@classmethod 这里后置放哪里都可以 你也可以放到最后一条用例后面去。
print("我是类级别的后置")
def test_01(self):
print("用例001")
def test_02(self):
print("用例002")
def test_03(self):
print("用例003")
if __name__ == '__main__':
unittest.main()
b) 函数级别(以函数级别的运行)
二、函数级别(以函数级别的运行)
前置:每个测试用例执行之前执行一次 —— def setUp(self) -> None: (固定写法)
后置:每个测试用例执行之后执行一次 —— def tearDown(self) -> None:(固定写法)
解释:
函数级别的前后置就是,比如你的测试用例有3条,那就每一条用例都会去执行前后置,
执行顺序:前置,第一条用例,后置;前置 ,第二条用例,后置
b) 示例:函数级别
例子:
class Test_setp(unittest.TestCase): #前置后置都需要类继承unittest.TestCase
def setUp(self) -> None:#这里-> None是表示前置是没有返回值的
print("我是函数级别的前置")
def test_01(self): #中间可以放测试用例
print("我是测试用例01")
def tearDown(self) -> None:#这里-> None是表示后置是没有返回值的
print("我是函数级别的后置")
if __name__ == '__main__':
unittest.main()
六、前置后置(TestFixture)
1、用例执行按照Ascii码顺序:0-9<A-Z<a-z
#打印对应字符的ascii码
print(ord("a")) #97#将ascii转换为对应的字符
print(chr(102))
七、用例断言(继承TestCase类的断言方法)
断言的特点:
1、unittest以程序运行过程中是否抛出异常来判断用例是否执行成功
2、如果断言失败程序会抛出异常,框架就会把这个用例标记为失败
3、只要你的程序中执行出现了其他异常,框架也会去捕获异常并将用例标记为失败例:self.assertEqual(1,1) 断言是否相等
unitest常见断言:
常见断言(unitest独有,pytest没有):
1、assertEqual(a, b)
# a == b
'''self.assertEqual(1 + 1, 2)#实际结果、预期结果是否相等'''
2、assertNotEqual(a, b)
# a != b
'''self.assertNotEqual(1, 2)#实际结果、预期结果是否不相等'''
3、assertTrue(x)
# bool(x) is True
'''self.assertTrue([1,2,3])#任何为真的表达式 非0 非空 true'''
4、assertFalse(x)
# bool(x) is False
'''self.assertFalse(0)#任何为假的表达式 0 空 false'''
5、assertIs(a, b)
# a is b
'''self.assertIs(1,1)#is身份运算符,a 是和 b引用自同一个对象'''
6、assertIsNot(a, b)
# a is not b
'''self.assertIsNot(1,2)#is not身份运算符,a 和 b不是引用自同一个对象'''
7、assertIn(a, b)
# a in b
'''self.assertIn(1,[1,2,3])#in成员运算符,a是b中一员,a在b中'''
8、assertNotIn(a, b)
# a not in b
'''self.assertNotIn(2,[1,3,4])#in成员运算符,a不是b中一员,a不在b中'''
9、assertIsInstance(a, b)
# isinstance(a, b)
'''self.assertIsInstance(123,int)#isinstance判断一个对象是否属于某个类或类型'''
10、assertNotIsInstance(a, b)
# not isinstance(a, b)
'''self.assertNotIsInstance([1,2,3],dict)#not isinstance判断一个对象不是否属于某个类或类型'''
11、assertGreater(a, b)
# a > b
'''self.assertGreater(5,2)#Greater判断a是否大于b'''
12、assertGreaterEqual(a, b)
# a >= b
'''self.assertGreaterEqual(5,5)#GreaterEqual判断a是否大于等于b'''
13、assertLess(a, b)
# a < b
'''self.assertLess(1,4)#less判断a是否小于b'''
14、assertLessEqual(a, b)
# a <= b
self.assertLessEqual(2,2)#less判断a是否小于等于b
unitest断言示例:
例子:
测试用例中:
def test_03(self):
print("用例003")
self.assertEqual(1+1,3)#实际结果、预期结果是否相等,如:1+1的值 是否 等于3——这里错了后,会报错,用例执行就会不通过
八、异常捕获与异常抛出
try...except 捕获到的异常一定要抛出来给框架,否则框架就会认为用例执行成功的
异常捕获示例:
2、手动抛异常:raise AssertionError(e)
def test_03(self):
try:
time.sleep(1)
self.assertEqual(1,2)
print("测试用例03")
except Exception as e:
# print("错误原因:",e)
raise AssertionError(e)
def test_03(self):
try:
self.assertEqual(1,2)
print("用例003")
except Exception as e:
raise AssertionError(e)
九、用例的收集(TestSuite)
a) 测试用例维度【了解】
添加一个测试用例
suite.addTest(TestDemo("test_04"))
添加多个测试用例
tests = [TestDemo("test_04"),TestDemo("test_02")]
suite.addTests(tests)
a) 示例:测试用例维度
添加单条用例:
from day16.unnitest_demo1 import Test_class_setup #先导入测试用例
import unittest#导入unitest库
suite = unittest.TestSuite() #创建测试套件进行收集用例
suite.addTest(Test_class_setup("test_01")) #添加测试用例到套件内,这里是测试用例为单位
run_now = unittest.TextTestRunner() #创建执行器
run_now.run(suite) #执行器中添加测试套件后 进行运行
添加多条用例:
from day16.unnitest_demo1 import Test_class_setup #先导入测试用例
import unittest#导入unitest库
suite = unittest.TestSuite() #创建测试套件进行收集用例
case = [Test_class_setup("test_01"),Test_class_setup("test_02")] #多条测试用例,用[]装起来也可以
suite.addTests(case) # 多条用addTests
run_now = unittest.TextTestRunner() #创建执行器
run_now.run(suite) #执行器中添加测试套件后 进行运行
b) 测试类维度【了解】
添加某个测试类(类里面所有的测试用例都会被执行)
suite.addTest(unittest.makeSuite(TestDemo))当只要执行py文件中多个测试用例中的几个,而不是全部执行时,那么使用testsuite的addtest方法加载指定的测试用例
当执行所有的py文件中的所有的测试用例时,那么使用TestLoader
b) 示例:测试类维度
添加测试类级别的用例:
from day17.wjh_test import Test_wjh
import unittest
suite = unittest.TestSuite() #实施例套件
test_case = unittest.TestLoader().loadTestsFromTestCase(Test_wjh)
#用这个方法,Python3不能用unittest.makeSuite(TestDemo)
#TestLoader:可以从指定目录查找指定的py文件中的所有测试用例,自动加载到TestSuite中
suite.addTest(test_case)
run1 = unittest.TextTestRunner()
run1.run(suite)
c) 模块为维度【掌握】
start_dir:测试文件目录
pattern='test*.py':测试用例文件名称,默认以test开头的py文件
case_dir = os.path.dirname(__file__)
suite = unittest.defaultTestLoader.discover(start_dir=case_dir)#指定某个目录下的某一类py文件进行运行,且运行所有测试用例
c) 示例:模块为维度
添加测试模块(用例文件)维度的用例————重点重点重点:
# 3、模块为维度【掌握】
# start_dir:测试文件目录
# pattern='test*.py':测试用例文件名称,默认以test开头的py文件
# case_dir = os.path.dirname(__file__)
# suite = unittest.defaultTestLoader.discover(start_dir=case_dir)
import os
from day17.wjh_test import Test_wjh #导入测试用例
from day17.wjh_test import Test_wjh01#导入测试用例
import unittest
case_dir = os.path.dirname(__file__) #当前目录路径
suite = unittest.defaultTestLoader.discover(start_dir=case_dir,pattern='wjh_start*.py')
#start_dir是函数中的参数,指:start_dir
#pattern是函数中的参数,指的:如(pattern='test*.py':测试用例文件名称,默认以test开头的py文件)
#默认可以不加pattern的参数,如果不加。他就默认找目录中 test文件开头py文件中的测试用例执行
run1 = unittest.TextTestRunner()#TextTestRunner执行的是TestSuite
run1.run(suite)
c) TextTestRunner 和 TestRunner() 区别
TextTestRunner 和 TestRunner() 区别,因为这里很多人以为这两者都是unitest的执行器
TextTestRunner:是执行TestSuite,也就是测试套件的用例
TestRunner():执行生成测试报告
十、数据驱动:DDT
数据驱动
一、什么叫数据驱动?
1、业务流程是固定的,变化的是业务中的数据
2、使用场景:业务流程一样,只是请求数据不一样
准备工作:
安装ddt(unitest自带的ddt驱动方式)
pip install ddt安装ddt(unittestreport第三库(木森大佬写的))
pip install unittestreport
a)unitest自带的ddt驱动方式
unitest自带的ddt驱动方式——例子:
import unittest
from ddt import ddt,data #导入ddt和data
test_cases = [{"api":"/api/login","user":"admin"},{"api":"/api/login","user":"test"}] #列表嵌套字典
@ddt #语法糖,使用ddt装饰器
class Test_login(unittest.TestCase):#同样需要继承unittest.TestCase
@data(*test_cases) #这里@data()表示需要添加数据进去,@data(*test_cases) 之所以加*号,是因为要解包,test_cases是列表嵌套字典
def test_01(self,data): #这里一定主要 需要把数据驱动的变量添加进来,比如这里的data
print("驱动")
print(data["api"])#这里随便插一句,所有运行时,鼠标光标放在类外面去,不然会报错,这是个大坑,unitest垃圾!
if __name__ == '__main__':
unittest.main()
b)unittestreport第三库(木森大佬写的)驱动方式
unittestreport(森哥)三方库的数据驱动
import unittest
from unittestreport import ddt,list_data #list_data是这个库的专门名字,主要就是为了省去解包的步骤,下面我们有讲解
test_cases = [{"api":"/api/login","user":"admin"},{"api":"/api/login","user":"test"}] #列表嵌套字典
@ddt #语法糖,使用ddt装饰器
class Test_login(unittest.TestCase):#同样需要继承unittest.TestCase
@list_data(test_cases) #@list_data()表示需要添加数据进去,这里不需要再解包上面的数据,因为@list_data有解包动作
def test_01(self,data): #这里一定主要 需要把数据驱动的变量添加进来,比如这里的data
print("驱动")
print(data["api"])#这里随便插一句,所有运行时,鼠标光标放在类外面去,不然会报错,这是个大坑,unitest垃圾!
if __name__ == '__main__':
unittest.main()
十一、unittestreport测试报告,并发送邮件
a) unitest自带测试报告——不推荐,丑,不好用
1、BeautifulReport(unitest官网自带的报告)
pip install BeautifulReport==0.1.3
b) unittestreport :木森自研(重点掌握)
unittestreport 使用文档unittestreport 使用文档unittestreport 使用文档:https://unittestreport.readthedocs.io/en/latest/doc8_thread/
安装unittestreport:pip install unittestreport
b) 示例:unittestreport
实例:
import unittest
from unittestreport import TestRunner#导入测试报告模块
case_dir = os.path.dirname(__file__) #当前目录路径
suite = unittest.defaultTestLoader.discover(start_dir=case_dir)#pattern='wjh_start*.py' #收集用例
runner = TestRunner( #实例化测试报告模块,填写相关信息
suite=suite,
filename="my_report.html",
report_dir="./reports",
title='测试报告',
tester='小阿卷',
desc="接口自动化项目测试报告",
templates=3
)
runner.run()
runner.send_email(
host="smtp.qq.com",
port=465,
user="123456@qq.com",
password="123123",
to_addrs=["123456@qq.com"]
)