天天看點

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

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

課程目錄

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擷取

PDF下載下傳:連結:https://pan.baidu.com/s/1OwAa8nl1eeBj8fcrgd3sBA 密碼:e9d8

本節内容

  • 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://127.0.0.1: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,用浏覽器打開即可
源碼下載下傳請聯系作者

繼續閱讀