注意:
接口测试的本质:测试类里面的函数,通过数据驱动
单元测试的本质:测试函数,代码级别,通过代码级别
单元测试框架:unittest+python
python+unittest+HTMLTestRunner写的,这套框架主要是针对单元测试的。其中HTMLTestRunner 是 Python 标准库的 unittest 模块的一个扩展,它可以生成 html的测试报告,而unittest作为标准python中的一个模块,是其它框架和工具的基础,所以了解unittest的原理对理解这套框架非常重要
功能测试步骤:
1、写用例(TestCase)
2、执行用例(TestSuite:存储用例,TestLoader:找用例,加载用例,存储到TestSuite里面)
3、对比实例结果,预期结果,判断用例是否通过(断言Assert)
4、出具测试报告(TextTestRunner)
如下,写个简单的加法/乘法运算,来说明单元测试:
1、框架:
2、Math_Method脚本:
class MathMethod(object):
def __init__(self,a,b):
self.a = a
self.b = b
def add(self):
return (self.a+self.b)
def multi(self):
return self.a * self.b
3、用例脚本
#写一个测试类,对我们写的MathMethod进行单元测试
import unittest
from class_5_18.Math_Method import MathMethod #测试目标类
class TestMathMethod(unittest.TestCase):
#编写测试用例
#一个测试用例就是一个函数,不能传参
#所有的用例都是test开头:test_
def test_add_two_positive(self): #两个正数相加
res = MathMethod(1,2).add()
print ("1+2的结果是:%s"%res)
#添加断言,期望结果与实际结果比对,一致通过,不一致失败
self.assertNotEqual(3,res,"测试成功") #断言里面的msg是用例执行失败了,才会显示
class TestMutli(unittest.TestCase):
def test_mutli_two_positive(self): # 两个正数相乘
res = MathMethod(1,2).multi()
print ("1*2的结果是:%s"% res)
#运行顺序按照ASCII码执行
if __name__ == "__mian__":
unittest.main()
4.1、执行用例脚本(
import unittest
import HTMLTestRunner
from class_5_18.class_01 import TestMathMethod
suite = unittest.TestSuite() #存储用例
#只执行一条
suite.addTest(TestMathMethod("test_add_two_positive")) #通过函数名执行用例
with open("test.txt","w+") as file:
runner = unittest.TextTestRunner(stream=file, verbosity=1) # verbosity表示打印的结果详细情况
runner.run(suite)
此方法只能通过函数名执行用例,如果用例有很多,几千个的话,此方法就行不通了
4.2、执行用例(通过loadTestsFromTestCase方法,可执行一个类名里面的全部用例)
import unittest
import HTMLTestRunner
from class_5_18.class_01 import TestMathMethod
from class_5_18 import class_01 #具体到模块
suite = unittest.TestSuite() #存储用例
#执行一个类里面的所有函数
loader = unittest.TestLoader() #创建一个加载器
suite.addTest(loader.loadTestsFromTestCase(TestMathMethod)) #根据测试类名加载(具体到类名)
with open("test.txt","w+") as file:
runner = unittest.TextTestRunner(stream=file, verbosity=1) # verbosity表示打印的结果详细情况
runner.run(suite)
4.3、执行用例(loadTestsFromModule此方法,如果一个模块里面有多个类,不能用类名执行,就得执行整个模块)
import unittest
import HTMLTestRunner
from class_5_18.class_01 import TestMathMethod
from class_5_18 import class_01 #具体到模块
suite = unittest.TestSuite() #存储用例
loader = unittest.TestLoader() #创建一个加载器
#执行一个模块里面的所有类,此方法需要导入模块
suite.addTest(loader.loadTestsFromModule(class_01)) #根据模块名使用,如果某个模块里面有多个类名,就需要使用此方法
with open("test.txt","w+") as file:
runner = unittest.TextTestRunner(stream=file, verbosity=1) # verbosity表示打印的结果详细情况值:0,1,2,2最详细
runner.run(suite)
4.4、因为如上报告,不容易看,所以引用HTMLTestRunner,可生成html报告,代码如下:
with open("test_report.html","wb") as file:
runner = HTMLTestRunner.HTMLTestRunner(
stream = file,
verbosity = 2,
title = "单元测试报告",
description = "python单元测试报告-第一次",
tester="么么哒"
)
runner.run(suite)
执行上述代码会报错:UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe5 in position 211: ordinal not in range(128)
是因为:混淆了 python2 里边的 str 和 unicode 数据类型。
0. 你需要的是让编码用实际编码而不是 ascii
import sys
reload(sys)
sys.setdefaultencoding('utf8')
把 str 编码由 ascii 改为 utf8 (或 gb18030)
如下生成测试报告:
异常处理:
def test_add_two_positive(self): #两个正数相加
res = MathMethod(1,2).add()
print ("1+2的结果是:%s"%res)
#添加断言,期望结果与实际结果比对,一致通过,不一致失败
try:
self.assertNotEqual(3,res,"测试成功") #断言里面的msg是用例执行失败了,才会显示
except Exception as e:
print ("断言出错了{0}".format(e))
raise e #出错了,需要抛出异常
1.什么是setUp()和tearDown()函数?
♦ setUp()函数是在众多函数或者说是在一个类类里面最先被调用的函数,而且每执行完一个函数都要从setUp()调用开始后再执行下一个函数,有几个函数就调用他几次,与位置无关,随便放在那里都是他先被调用。
♦ tearDown()函数是在众多函数执行完后他才被执行,意思就是不管这个类里面有多少函数,他总是最后一个被执行的,与位置无关,放在那里都行,最后不管测试函数是否执行成功都执行tearDown()方法;如果setUp()方法失败,则认为这个测试项目失败,不会执行测试函数也不执行tearDown()方法。
2.为什么我们要用setUp()和tearDown()函数?
♦ 我们利用这一特性在自动化中setup主要是进行测试前的初始化工作,比如在接口测试前面做一些前置的参数赋值,数据库操作等等 teardown是测试后的清除工作,比如参数还原或销毁,数据库的还原恢复等
def setUPModule(self): #整个模块只执行一次
pass
def teatDownModule(self):#整个模块只执行一次
pass
def setUp(self): #每个用例执行一次
pass
def tearDown(self): #每个用例执行一次
pass
@staticmethod
def setUpClass(cls): #当前类执行一次
pass
@classmethod
def tearDownClass(cls): #当前类执行一次
pass
python模块化setUp()、tearDown()、setUpClass()、tearDownClass()、setUpModule()、tearDownModule()的区别
setUp():每个测试方法运行前运行,测试前的初始化工作。一条用例执行一次,若N次用例就执行N次,根据用例的数量来定。
tearDown():每个测试方法运行结束后运行,测试后的清理工作。一条用例执行一次,若N次用例就执行N次。
setUpClass():所有的测试方法运行前运行,为单元测试做前期准备,但必须使用@classmethod装饰器进行修饰,整个测试过程中只执行一次。
tearDownClass():所有的测试方法运行结束后运行,为单元测试做后期清理工作,但必须使用@classmethod装饰器进行修饰,整个测试过程中只执行一次。
setUpModule():模块只指的是作用于一个文件,整个文件级别上只调用一次
tearDownModule():模块只指的是作用于一个文件,整个文件级别上只调用一次
第一种:在setUp里面生成cookie,方便后面调用
class Login(unittest.TestCase):
def setUp(self):
#第一种,在setUp里面生成cookie,方便后面调用
self.url = 'https://www.ketangpai.com/UserApi/login' #加self表示这个属性可以被实例调用,可以在类函数类面被调用
self.data = {"email": "1884xxxxx", "password": "abc123456"}
#开通VIP的接口,需提前登录
self.vip_url = "https://www.ketangpai.com/VipApi/getProductLists"
self.cookies = HttpRequest().http_request(self.url,'post',self.data).cookies
def tearDown(self):
pass
def test_001(self):
data = {"email": "188464xxxxx","password": "abc123456"}
res = HttpRequest().http_request(self.url,"post",data)
print ("打印结果是{0}".format(res.json()))
def test_002(self):
data = {"email": "18846xxxxx", "password": "123456"}
res = HttpRequest().http_request(self.url, "post", data)
print ("打印结果是{0}".format(res.json()))
def test_003(self):
vip_res = HttpRequest().http_request(self.vip_url,'get',data={},cookie=self.cookies)
print ("打印结果是{0}".format(vip_res.json()))
if __name__=='__main__':
unittest.main()
第二种,把cookie定义为全局变量
COOKIE = None #定义全局变量
class Login(unittest.TestCase):
def setUp(self):
self.url = 'https://www.ketangpai.com/UserApi/login' #
self.vip_url = "https://www.ketangpai.com/VipApi/getProductLists"
def tearDown(self):
pass
def test_001(self):
#第二种,定义全局变量
global COOKIE #申明全局变量
data = {"email": "18846450275","password": "abc123456"}
res = HttpRequest().http_request(self.url,"post",data)
if res.cookies: #如果又cookie,则更新COOKIE
COOKIE = res.cookies
def test_002(self):
global COOKIE
data = {"email": "18846450275", "password": "123456"}
res = HttpRequest().http_request(self.url, "post", data)
print ("打印结果是{0}".format(res.json()))
def test_003(self):
global COOKIE
vip_res = HttpRequest().http_request(self.vip_url,'get',data={},cookie=COOKIE)
print ("打印结果是{0}".format(vip_res.json()))
if __name__=='__main__':
unittest.main()
第三种:反射
get_data脚本
class GetData:
Cookie = None #存储cookie,初始值为None
from class_5_18.get_data import GetData
class Login(unittest.TestCase):
def setUp(self):
self.url = 'https://www.ketangpai.com/UserApi/login' #加self表示这个属性可以被实例调用,可以在类函数类面被调用
self.vip_url = "https://www.ketangpai.com/VipApi/getProductLists"
def tearDown(self):
pass
def test_001(self):
data = {"email": "18846450275","password": "abc123456"}
res = HttpRequest().http_request(self.url,"post",data)
if res.cookies: #如果又cookie,则更新COOKIE
setattr(GetData,'Cookie',res.cookies) #设置Cookie的属性值为res.cookies
print ("打印结果是{0}".format(res.json()))
def test_002(self):
data = {"email": "18846450275", "password": "123456"}
res = HttpRequest().http_request(self.url, "post", data)
print ("打印结果是{0}".format(res.json()))
def test_003(self):
vip_res = HttpRequest().http_request(self.vip_url,'get',data={},cookie=getattr(GetData,'Cookie'))
print ("打印结果是{0}".format(vip_res.json()))
if __name__=='__main__':
unittest.main()