1. 程式人生 > >Python接口測試實戰3(下)- unittest測試框架

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

module stream 打開 moudle 解壓 none 寫入 報告 use

如有任何學習問題,可以添加作者微信:lockingfree

課程目錄

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

更多學習資料請加QQ群: 822601020獲取

本節內容

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

unitttest簡介

參考:unittest官方文檔

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

unittest特點

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

unittest幾大組成部分

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

用例編寫

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

test_user_login.py # 文件必須test_開頭

import unittest  # 導入unittest
import requests

class TestUserLogin(unittest.TestCase):  # 類必須Test開頭,繼承TestCase才能識別為用例類
    url = ‘http://115.28.108.130: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__‘:  # 如果是直接從當前模塊執行(非別的模塊調用本模塊)
    unittest.main(verbosity=2)    # 運行本測試類所有用例,verbosity為結果顯示級別

用例執行順序:並非按書寫順序執行,而是按用例名ascii碼先後順序執行

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

  • 判斷相等
    • assertEqual(a,b)/assertNotEqual(a,b): 斷言值是否相等
    • assertIs(a,b)/assertIsNot(a,b): 斷言是否同一對象(內存地址一樣)
    • 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(測試清理)方法,用於分別在測試前及測試後執行

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

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

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

def tearDownModule(): # 當前模塊執行後只執行一次
    print(‘=== tearDownModule ===‘)

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

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

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

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

執行結果:

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

OK

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

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

test_user_reg.py

import unittest
import requests
from db import *   # 導入db.py文件,源碼見上篇

# 數據準備
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):
        # 環境檢查
        if check_user(NOT_EXIST_USER):
            del_user(NOT_EXIST_USER)
        
        # 發送請求
        data = {‘name‘: NOT_EXIST_USER, ‘password‘: ‘123456‘}
        res = requests.post(url=self.url, json=data)
        
        # 期望響應結果,註意字典格式和json格式的區別(如果有true/false/null要轉化為字典格式)
        except_res = {
                        "code": "100000",
                        "msg": "成功",
                        "data": {
                                    "name": NOT_EXIST_USER,
                                    "password": "e10adc3949ba59abbe56e057f20f883e"
                                }
                      }
        
        # 響應斷言(整體斷言)
        self.assertDictEqual(res.json(), except_res)
        
        # 數據庫斷言
        self.assertTrue(check_user(NOT_EXIST_USER))
        
        # 環境清理(由於註冊接口向數據庫寫入了用戶信息)
        del_user(NOT_EXIST_USER)
        
    def test_user_reg_exist(self):
         # 環境檢查
        if not check_user(EXIST_USER):
            add_user(EXIST_USER)
        
        # 發送請求
        data = {‘name‘: EXIST_USER, ‘password‘: ‘123456‘}
        res = requests.post(url=self.url, json=data)
        
        # 期望響應結果,註意字典格式和json格式的區別(如果有true/false/null要轉化為字典格式)
        except_res = {
                        "code": "100001",
                        "msg": "失敗,用戶已存在",
                        "data": {
                                    "name": EXIST_USER,
                                    "password": "e10adc3949ba59abbe56e057f20f883e"
                                }
                      }
        
        # 響應斷言(整體斷言)
        self.assertDictEqual(res.json(), except_res)
        
        # 數據庫斷言(沒有註冊成功,數據庫沒有添加新用戶)
        
        # 環境清理(無需清理)
        
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 # 從上面兩個例子裏導入測試類

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(用例發現)遍歷所有的用例
import unittest

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

註意:

  • 子目錄中需要包含__init__.py文件,及應為的Python包
  • 所有用例因為test_*.py,包含測試類應以Test開頭,並繼承unittest.TestCase, 用例應以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("./")

# 輸出測試結果到文本文件
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,用瀏覽器打開即可

技術分享圖片

技術分享圖片

源碼下載鏈接:https://pan.baidu.com/s/1gXwYOCL6BfRW4XVgxkCpIA 密碼:jdgo

此為北京龍騰育才 Python高級自動化(接口測試部分)授課筆記
課程介紹
想要參加現場(北京)/網絡課程的可以聯系作者微信:lockingfree

  1. 高效學習,快速掌握Python自動化所有領域技能
  2. 同步快速解決各種問題
  3. 配套實戰項目練習

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