Python接口測試實戰(zhàn)3(下)- unittest測試框架

簡書已停更,歡迎轉(zhuǎn)到個人博客查看對應(yīng)教程:https://www.cnblogs.com/superhin/p/10338993.html

課程目錄

Python接口測試實戰(zhàn)1(上)- 接口測試理論
Python接口測試實戰(zhàn)1(下)- 接口測試工具的使用
Python接口測試實戰(zhàn)2 - 使用Python發(fā)送請求
Python接口測試實戰(zhàn)3(上)- Python操作數(shù)據(jù)庫
Python接口測試實戰(zhàn)3(下)- unittest測試框架
Python接口測試實戰(zhàn)4(上) - 接口測試框架實戰(zhàn)
Python接口測試實戰(zhàn)4(下) - 框架完善:用例基類,用例標簽,重新運行上次失敗用例
Python接口測試實戰(zhàn)5(上) - Git及Jenkins持續(xù)集成
Python接口測試實戰(zhàn)5(下) - RESTful、Web Service及Mock Server

更多學(xué)習(xí)資料請加添加作者微信:lockingfree獲取

本節(jié)內(nèi)容

  • unittest簡介
  • 用例編寫
  • 用例組織及運行
  • 生成測試報告

unitttest簡介

參考:unittest官方文檔

為什么要使用unittest?
在編寫接口自動化用例時,我們一般針對一個接口建立一個.py文件,一條測試用例封裝為一個函數(shù)(方法),但是在批量執(zhí)行的過程中,如果其中一條出錯,后面的用例就無法執(zhí)行。使用測試框架可以互不影響的用例執(zhí)行及更靈活的執(zhí)行控制

unittest特點

  • python自帶的單元測試框架,無需安裝
  • 用例執(zhí)行互不干擾
  • 提供不同范圍的setUp(測試準備)和tearDown(測試清理)方法
  • 提供豐富的斷言方法
  • 可以通過discover批量執(zhí)行所有模塊的用例
  • 可以通過TestSuite(測試集)靈活的組織用例

unittest幾大組成部分

  • TestCase: 用例對象,編寫測試用例時要繼承該類,以具有TestCase的屬性和方法
  • TestSuite: 測試集或測試套件,測試用例的集合,用來組織用例,支持嵌套
  • TestLoader: 用例加載器,用于向TestSuite中添加用例
  • TextTestRunner: 用例執(zhí)行器(輸出文本結(jié)果),一般以TestSuite為單位執(zhí)行用例
  • TestResult: 測試結(jié)果

用例編寫

  1. 新建一個test_開頭(必須)的.py文件,如test_user_login.py
  2. 導(dǎo)入unittest
  3. 編寫一個Test開頭(必須)的類,并繼承unittest.TestCase,做為測試類
  4. 在類中編寫一個test_開頭(必須)的方法,作為用例

test_user_login.py # 文件必須test_開頭

import unittest  # 導(dǎo)入unittest
import requests

class TestUserLogin(unittest.TestCase):  # 類必須Test開頭,繼承TestCase才能識別為用例類
    url = 'http://127.0.0.1:5000/api/user/login/'
    
    def test_user_login_normal(self):   # 一條測試用例,必須test_開頭
        data = {"name": "張三", "password": "123456"}
        res = requests.post(url=self.url, data=data)
        self.assertIn('登錄成功', res.text)  # 斷言
        
    def test_user_login_password_wrong(self):
        data = {"name": "張三", "password": "1234567"}
        res = requests.post(url=self.url, data=data)
        self.assertIn('登錄失敗', res.text)  # 斷言


if __name__ == '__main__':  # 如果是直接從當前模塊執(zhí)行(非別的模塊調(diào)用本模塊)
    unittest.main(verbosity=2)    # 運行本測試類所有用例,verbosity為結(jié)果顯示級別

用例執(zhí)行順序:并非按書寫順序執(zhí)行,而是按用例名ascii碼先后順序執(zhí)行

用例斷言
unittest提供了豐富的斷言方法,常用為以下幾種:

  • 判斷相等
    • assertEqual(a,b)/assertNotEqual(a,b): 斷言值是否相等
    • assertIs(a,b)/assertIsNot(a,b): 斷言是否同一對象(內(nèi)存地址一樣)
    • assertListEqual(list1, list2)/assertItemNotEqual(list1, list2): 斷言列表是否相等
    • assertDictEqual(dict1, dict2)/assertDictNotEqual(dict1, dict2): 斷言字典是否相等
  • 是否為空
    • assertIsNone(a)/assertIsNotNone(a)
  • 判斷真假
    • assertTrue(a)/assertFalse(a)
  • 是否包含
    • assertIn(a,b)/assertNotIn(a,b) # b中是否包含a
  • 大小判斷
    • assertGreater(a,b)/assertLess(a,b) : 斷言a>b / 斷言a<b
    • assertGreaterEqual(a,b)/assertLessEqual: 斷言a>=b / 斷言a<=b
  • 類型判斷
    • assertIsInstance(a,dict)/assertNotIsInstance(a,list) # 斷言a為字典 / 斷言a非列表

示例:

import unittest

case = unittest.TestCase()
case.assertEqual(1,2.0/2) # 通過1=2.0/2
case.assertEqual(1, True) # 通過
case.assertIs(1.0, 2.0/2) # 失敗,不是同一對象
case.assertListEqual([1,2],[1,2]) # 通過(順序要一致)
case.assertDictEqual({"a":1,"b":2},{"b":2,"a":1}) # 通過,字典本無序
case.assertIsNone({}) # 失敗
case.assertFalse({}) # 通過,空字典為False
case.assertIn("h","hello") # 通過
case.assertGreater(3,2) # 通過,3>2
case.assertIsInstance({"a":1}, dict) # 通過

斷言是unittest.TestCase的一種方法,通過斷言判斷用例是否通過(Pass/Fail)

Test Fixtures(用例包裹方法)
Test Fixtures即setUp(用例準備)及tearDown(測試清理)方法,用于分別在測試前及測試后執(zhí)行

按照不同的作用范圍分為:

  • setUp()/tearDown(): 每個用例執(zhí)行前/后執(zhí)行一次
  • setUpClass()/tearDownClass(): 每個測試類加載時/結(jié)束時執(zhí)行一次
  • setUpMoudle()/tearDownMoudle(): 每個測試模塊(一個py文件為一個模塊)加載/結(jié)束時執(zhí)行一次
import unittest

def setUpModule():    # 當前模塊執(zhí)行前只執(zhí)行一次
    print('=== setUpModule ===')

def tearDownModule(): # 當前模塊執(zhí)行后只執(zhí)行一次
    print('=== tearDownModule ===')

class TestClass1(unittest.TestCase):
    @classmethod          # 聲明為類方法(必須)
    def setUpClass(cls):  # 類方法,注意后面是cls,整個類只執(zhí)行一次
        print('--- setUpClass ---')

    @classmethod
    def tearDownClass(cls):  
        print('--- tearDownClass ---')
        
    def setUp(self):  # 該類中每個測試用例執(zhí)行一次
        print('... setUp ...')
        
    def tearDown(self):
        print('... tearDown ...')
        
    def test_a(self):  # 測試用例
        print("a")
        
    def test_B(self): # 大寫B(tài)的ascii比小寫a靠前,會比test_a先執(zhí)行
        print("B")

class TestClass2(unittest.TestCase):  # 該模塊另一個測試類
    def test_A(self):
        print("A")

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

執(zhí)行結(jié)果:

=== setUpModule ===
--- setUpClass ---
... setUp ...
B
... tearDown ...
... setUp ...
a
... tearDown ...
--- tearDownClass ---
A
=== tearDownModule ===
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

完整的接口測試用例
一條完整的測試接口用例需要包含:

  1. 數(shù)據(jù)準備:準備測試數(shù)據(jù),可手工準備,也可使用代碼準備(通常涉及數(shù)據(jù)庫操作)
  2. 環(huán)境檢查:如果手工準備的數(shù)據(jù),連接數(shù)據(jù)庫進行環(huán)境檢查會使用例更健壯
  3. 發(fā)送請求:發(fā)送接口請求
  4. 響應(yīng)斷言/數(shù)據(jù)庫斷言:響應(yīng)斷言后,還需要進行數(shù)據(jù)庫斷言,以確保接口數(shù)據(jù)庫操作的正確性
  5. 數(shù)據(jù)清理:如果接口有更數(shù)據(jù)庫操作,斷言結(jié)束后需要還原更改

test_user_reg.py

import unittest
import requests
from db import *   # 導(dǎo)入db.py文件,源碼見上篇

# 數(shù)據(jù)準備
NOT_EXIST_USER = '范冰冰'
EXIST_USER = '張三'


class TestUserReg(unittest.TestCase):
    url = 'http://115.28.108.130:5000/api/user/reg/'
    
    def test_user_reg_normal(self):
        # 環(huán)境檢查
        if check_user(NOT_EXIST_USER):
            del_user(NOT_EXIST_USER)
        
        # 發(fā)送請求
        data = {'name': NOT_EXIST_USER, 'password': '123456'}
        res = requests.post(url=self.url, json=data)
        
        # 期望響應(yīng)結(jié)果,注意字典格式和json格式的區(qū)別(如果有true/false/null要轉(zhuǎn)化為字典格式)
        except_res = {
                        "code": "100000",
                        "msg": "成功",
                        "data": {
                                    "name": NOT_EXIST_USER,
                                    "password": "e10adc3949ba59abbe56e057f20f883e"
                                }
                      }
        
        # 響應(yīng)斷言(整體斷言)
        self.assertDictEqual(res.json(), except_res)
        
        # 數(shù)據(jù)庫斷言
        self.assertTrue(check_user(NOT_EXIST_USER))
        
        # 環(huán)境清理(由于注冊接口向數(shù)據(jù)庫寫入了用戶信息)
        del_user(NOT_EXIST_USER)
        
    def test_user_reg_exist(self):
         # 環(huán)境檢查
        if not check_user(EXIST_USER):
            add_user(EXIST_USER)
        
        # 發(fā)送請求
        data = {'name': EXIST_USER, 'password': '123456'}
        res = requests.post(url=self.url, json=data)
        
        # 期望響應(yīng)結(jié)果,注意字典格式和json格式的區(qū)別(如果有true/false/null要轉(zhuǎn)化為字典格式)
        except_res = {
                        "code": "100001",
                        "msg": "失敗,用戶已存在",
                        "data": {
                                    "name": EXIST_USER,
                                    "password": "e10adc3949ba59abbe56e057f20f883e"
                                }
                      }
        
        # 響應(yīng)斷言(整體斷言)
        self.assertDictEqual(res.json(), except_res)
        
        # 數(shù)據(jù)庫斷言(沒有注冊成功,數(shù)據(jù)庫沒有添加新用戶)
        
        # 環(huán)境清理(無需清理)
        
if __name__ == '__main__':
    unittest.main(verbosity=2)   # 運行所有用例

用例組織及運行

除了使用unittest.main()運行整個測試類之外,我們還可以通過TestSuite來靈活的組織要運行的測試集

  1. 新建TestSuite并添加測試用例
import unittest
from test_user_login import TestUserLogin  
from test_user_reg import TestUserReg # 從上面兩個例子里導(dǎo)入測試類

suite = unittest.TestSuite()
suite.addTest(TestUserLogin('test_user_login_normal')) # 添加單個用例
suite.addTests([TestUserReg('test_user_reg_normal'),TestUserReg('test_user_reg_exist')]) # 添加多個用例

# 運行測試集
unittest.TextTestRunner(verbosity=2).run(suite)  # verbosity顯示級別,運行順序為添加到suite中的順序
  1. 使用makeSuite來制作用例集
import unittest
from test_user_login import TestUserLogin

suite1 = unittest.makeSuite(TestUserLogin, 'test_user_login_normal') # 使用測試類的單條用例制作測試集
suite2 = unittest.makeSuite(TestUserLogin) # 使用整個測試類制作測試集合(包含該測試類所有用例)

unittest.TextTestRunner(verbosity=2).run(suite1)
  1. 使用TestLoader(用例加載器)生成測試集
improt unittest
from test_user_login import TestUserLogin

suite = unittest.TestLoader().loadTestsFromTestCase(TestUserLogin) # 加載該測試類所有用例并生成測試集

unittest.TextTestRunner(verbosity=2).run(suite)
  1. 使用discover(用例發(fā)現(xiàn))遍歷所有的用例
import unittest

suite = unittest.defaultTestLoader.discover("./")  # 遍歷當前目錄及子包中所有test_*.py中所有unittest用例
unittest.TextTestRunner(verbosity=2).run(suite)

注意:

  • 子目錄中需要包含__init__.py文件,及應(yīng)為的Python包
  • 所有用例因為test_*.py,包含測試類應(yīng)以Test開頭,并繼承unittest.TestCase, 用例應(yīng)以test_開頭
  1. 測試集嵌套
import unittest
from test_user_login import TestUserLogin

suite1 = unittest.TestSuite()
suite1.addTest(TestUserLogin('test_user_login_normal'))

suite2 = makeSuite(TestUserLogin, 'test_user_login_password_wrong')

suite = unittest.TestSuite([suite1, suite2])  # 將兩個測試集組合為一個

unittest.TextTestRunner(verbosity=2).run(suite)

生成測試報告

生成文本報告

import unittest

suite = unittest.defaultTestLoader.discover("./")

# 輸出測試結(jié)果到文本文件
with open("result.txt","w") as f:
    unittest.TextTestRunner(stream=f,verbosity=2).run(suite) # 將輸出流stream輸出到文件

生成HTML報告

  1. 下載HTMLTestRunnerCN
  2. 解壓并將解壓包中python3x文件夾下的HTMLTestRunnerCN.py拷貝到項目目錄
  3. 在目錄下新建腳本run_all.py
import unittest
from HTMLTestReportCN import HTMLTestRunner

suite = unittest.defaultTestLoader.discover("./")

f = open("report.html", 'wb') # 二進制寫格式打開要生成的報告文件
HTMLTestRunner(stream=f,title="Api Test",description="測試描述",runner="卡卡").run(suite)
f.close()
  1. 運行腳本,會在當前文件夾下生成report.html,用瀏覽器打開即可
項目預(yù)覽
測試報告
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容