python+unittest单元测试框架详解

注意:
接口测试的本质:测试类里面的函数,通过数据驱动
单元测试的本质:测试函数,代码级别,通过代码级别
单元测试框架:unittest+python

一、unittest简介

python+unittest+HTMLTestRunner写的,这套框架主要是针对单元测试的。其中HTMLTestRunner 是 Python 标准库的 unittest 模块的一个扩展,它可以生成 html的测试报告,而unittest作为标准python中的一个模块,是其它框架和工具的基础,所以了解unittest的原理对理解这套框架非常重要

二、基础流程

功能测试步骤:
1、写用例(TestCase)
2、执行用例(TestSuite:存储用例,TestLoader:找用例,加载用例,存储到TestSuite里面
3、对比实例结果,预期结果,判断用例是否通过(断言Assert)
4、出具测试报告(TextTestRunner)

如下,写个简单的加法/乘法运算,来说明单元测试:
1、框架:
python+unittest单元测试框架详解_第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

  1. 对需要 str->unicode 的代码,可以在前边写上
import sys 
reload(sys) 
sys.setdefaultencoding('utf8') 

把 str 编码由 ascii 改为 utf8 (或 gb18030)
如下生成测试报告:
python+unittest单元测试框架详解_第2张图片
异常处理:

    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()

你可能感兴趣的:(python基础)