天天看點

unittest學習(一)

學習筆記-unittest

筆記目錄:https://blog.csdn.net/weixin_42717928/article/details/114343085

昨天下了雨,今天刮了風,明天太陽就出來了

  • 簡介
  • 概念
  • 斷言
  • 其他相關
  • web自動化小案例

一:簡介

單元測試适合白盒或者開發人員做。

單元測試架構可以用來寫測試用例,也适合做Web自動化測試、App自動化測試、接口自動化測試等。

Python中有doctest、unittest、pytest、nose等單元測試架構。

(1)什麼是單元測試

本質是通過一段代碼去驗證另一段代碼,是以不要單元測試架構也可以寫單元測試。

class Calculation:
    """  完成加法計算 """
    def __init__(self, a, b):
        self.a = int(a)
        self.b = int(b)

    # 加法
    def add(self):
        return self.a + self.b
           
from test.test2 import Calculation


# 測試加法
def test_add():
    c = Calculation(1, 1)
    rs = c.add()
    # 這裡我特意斷言失敗
    assert rs == 3, '加法執行錯誤'


if __name__ == '__main__':
    test_add()
           

但這種方法有問題:1是要自己定義失敗的提示,2是如果某個測試函數執行失敗,後面的測試函數不再執行,3是無法統計結果(編寫更多代碼可以解決,但偏離測試的初衷)

使用unittest編寫測試用例:

import unittest
from test.test2 import Calculation


class TestCalculation(unittest.TestCase):

    def test_add(self):
        c = Calculation(1, 1)
        rs = c.add()
        self.assertEqual(rs, 3)


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

這裡有個坑,就是比如我想使用unittest運作單個用例,滑鼠在方法名右鍵執行就行,運作整個的,就在main那裡右鍵執行。(這種方式運作是如python tests那樣,因為使用了unittest,預設是使用這種方式運作)

但是這種方式是沒有測試報告的,必須這樣(如果不知道路徑,搞個簡單的輸出檔案複制一下就行)

unittest學習(一)

這樣輸出就有對應的結果了(也可以吧python tests的删除了,滑鼠在main後,就能看到test的run了)

unittest學習(一)

這裡結果的含義:

  • .:表示通過
  • E:表示運作錯誤
  • s:表示運作跳過
  • F:表示運作失敗

二:概念

Test Case:最小的測試單元,Unittest提供了TestCase用于建立測試用例

Test Suite:測試套件,用于組裝一組要運作的測試,Unittest提供了TestSuite用于建立測試套件

Test Runner:用于協調測試的執行并向使用者提供結果,Unittest提供了TextTestRunner用于運作測試用例,提供HTMLTestRunner用于生成HTML測試報告

Test Fixture:代表執行一個或多個測試所需的環境準備,以及關聯的清理動作。如setUp、tearDown

import unittest
from test.test2 import Calculation


# 必須繼承unittest子產品的TestCase類
class TestCalculation(unittest.TestCase):

    # 測試用例前置動作
    def setUp(self):
        print("測試開始")

    # 測試用例後置動作
    def tearDown(self):
        print("測試結束")

    # 測試方法以test開頭
    def test_add(self):
        print("第一條測試用例")
        c = Calculation(1, 1)
        rs = c.add()
        # 由于繼承了TestCase類,可以通過self直接調用斷言方法
        self.assertEqual(rs, 3)

    def test_sub(self):
        print("第二條測試用例")
        c = Calculation(1, 1)
        rs = c.sub()
        # 由于繼承了TestCase類,可以通過self直接調用斷言方法
        self.assertEqual(rs, 0)


if __name__ == '__main__':
    # 抛棄了這個方法
    # unittest.main()

    # 建立測試套件
    suit = unittest.TestSuite()
    suit.addTest(TestCalculation("test_add"))
    suit.addTest(TestCalculation("test_sub"))

    # 建立測試運作器
    runner = unittest.TextTestRunner()
    runner.run(suit)
           

好處是可以控制執行順序,還有需要運作的用例。

三:斷言

方法 檢查 版本
assertEqual(a,b) a==b
assertNotEqual(a,b) a!=b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a,b) a is b 3.1
assertIsNot(a,b) a is not b 3.1
assertIsNone(x) x is None 3.1
assertIsNotNone(x) x is not None 3.1
assertIn(a,b) a in b 3.1
assertNotIn(a,b) a not in b 3.1
assertIsInstance(a,b) Isinstance(a,b) 3.1
assertNotIsInstance(a,b) Not Isinstance(a,b) 3.1

可以通過這種方式推出斷言的使用:

import unittest


class TestAssert(unittest.TestCase):

    def test_equal(self):
        self.assertEqual(1+1, 2)


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

四:其他相關

(1)一個測試類對應一個被測試功能,如加法類,裡面有很多方法,如整數相加、小數相加等

格式:

unittest學習(一)
unittest學習(一)
unittest學習(一)

unittest中的TestLoader類提供了discover()方法可以從多個檔案中查找測試用例,

不需要建立該類執行個體,unittest提供了可共享的defaultTestLoader類,可以使用其子類或方法建立執行個體,再調用discover方法

import unittest

# 測試用例目錄
test_dir = './test'
# 測試用例目錄、比對以test開頭的多個檔案、還有一個參數是top_level_dir=None:測試子產品的頂層目錄,若沒有則預設是None
suits = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suits)
           

(2)測試用例的執行順序

測試用例的執行順序涉及多個層級:多個測試目錄>多個測試檔案>多個測試類>多個測試方法(測試用例)

unittest預設根據ASCII碼的順序加載測試用例(0~9,A~Z,a~z),這是main()方法執行的規律

discover()方法和main()一樣,對于測試目錄、測試檔案同樣适用

也可以通過測試套件TestSuite類的addTest()方法按照順序來加載執行

(3)執行多級目錄的測試用例

多個目錄,如果discover()方法中的start_dir參數定義為“./test”目錄,為了能查到更下層的目錄,可以在每個子目錄放一個__init__.py檔案。作用是将一個目錄記為一個标準的python子產品。

(4)跳過測試和預期失敗

不僅僅可以作用于方法,類也可以,下面是作用于方法

import unittest


class MyTest(unittest.TestCase):

    # 無條件跳過,需要說明跳過測試的原因
    @unittest.skip("直接跳過測試")
    def test_skip(self):
        print("1")

    @unittest.skipIf(2 > 1, "條件為真時跳過測試")
    def test_skip_if(self):
        print("2")

    @unittest.skipUnless(2 > 1, "條件為真時執行")
    def test_skip_unless(self):
        print("3")

    # 不管執行結果是否失敗,都将測試标為失敗,但不會抛出失敗資訊(這裡我看結果的符号是u)
    @unittest.expectedFailure
    def test_expected_failure(self):
        print("4")


if __name__ == '__main__':
    unittest.main()
           
unittest學習(一)

(5)Fixture

夾心餅幹的餅幹,setIp和tearDown,還有更大範圍的,如測試類和子產品的fIxture

import unittest

# setUpModule和tearDownModule在整個子產品的開始與結束執行
def setUpModule():
    print("1")

def tearDownModule():
    print("1-end")

class MyTest(unittest.TestCase):
    # setUpClass和tearDownClass在測試類的開始和結束執行
    # 都是類方法,使用classmethod裝飾,cls可以了解為和self一樣的表示第一個參數的意思
    @classmethod
    def setUpClass(cls):
        print("2")

    @classmethod
    def tearDownClass(cls):
        print("2-end")

    # setUp和tearDown在測試用例的開始和結束執行,是以先2-測試1-2-end,再2-測試2-2-end
    def setUp(self):
        print("3")

    def tearDown(self):
        print("3-end")

    def test_case1(self):
        print("測試1")

    def test_case2(self):
        print("測試2")


if __name__ == '__main__':
    unittest.main()
           
unittest學習(一)

五:web自動化小案例

import unittest
from time import sleep

from selenium import webdriver
from selenium.webdriver.common.by import By


class TestBaiDu(unittest.TestCase):

    # 使用這個比setUp好,不用每執行一次用例就重新開機和關閉一次浏覽器,提高效率
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.test_url = "https://www.baidu.com"

    # 封裝重複代碼(不是test開頭,不會當成用例去執行)
    def baidu_search(self, search_key):
        self.driver.get(self.test_url)
        self.driver.find_element(By.ID, "kw").send_keys(search_key)
        self.driver.find_element(By.ID, "su").click()
        sleep(2)

    # 用例1
    def test_search_haha(self):
        search_key = "haha"
        self.baidu_search(search_key)
        title = self.driver.title
        self.assertEqual(title, search_key+"_百度搜尋")

    # 用例2
    def test_search_hehe(self):
        search_key = "hehe"
        self.baidu_search(search_key)
        title = self.driver.title
        self.assertEqual(title, search_key+"_百度搜尋")

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


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

繼續閱讀