這個文檔的由來是公司內部UI自動化測試培訓的資料。部門爲了減少測試工作量,準備做UI自動化測試。我寫python,其他同事都是java,所以python基礎和UI自動化測試selenium的培訓就由我來完成。本篇是單元測試工具uniitest的基礎介紹。
UI 自動化測試相關內容:
unittest 介紹
Unittest是Python內部自帶的一個單元測試的模塊,它設計的靈感來源於Junit,具有和Junit類似的結構,有過Junit經驗的朋友可以很快上手。它可以做單元測試, 也能用於編寫和運行重複的測試工作。
它給自動化測試用例開發和執行提供了豐富的斷言方法, 判斷測試用例是否通過, 並最終生成測試結果。
TestCase 測試用例編寫
unittest 基本知識包括如下:
- TestCase指的就是測試用例
- 測試類必須繼承unittest.TestCase
- 測試方法名稱命名必須以test開頭
- 測試方法的執行順序由Case序號決定, 並非由代碼順序決定
# 1. 導入unittest
import unittest
# 2. 創建類繼承unittest.TestCase
class TestDemo(unittest.TestCase):
# 3. 創建測試用例方法, 方法要以test開頭
# 執行順序是根據case序號來的, 並非代碼的順序
def test_add_01(self):
print(3+2)
assert 3+2 == 5
def test_add_02(self):
print(5+5)
assert 5+5 == 10
if __name__ == '__main__':
unittest.main()
(dev) ➜ ui_test python unittest_demo.py
5
.10
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
unittest 特性
unittest 測試類中有一些特殊方法可以方便單元測試的環境準備和結束收尾,下面分別介紹。
單個測試用例
一個測試用例就是類似中的一個方法,啓動一個測試用例可能需要準備參數,準備環境。以下兩個方法可以用於當前場景:
- setUp 初始化測試環境, 它在每條測試用例執行前都會調用
- tearDown 測試用例執行完畢後恢復測試環境, 即使出現異常也會調用此方法,每條用例執行結束後都會運行
import unittest
class Test(unittest.TestCase):
def setUp(self) -> None: # 調用setUp
super().setUp()
print("測試用例執行前操作")
def test_add_01(self):
print("num02")
def test_add_02(self):
print("num03")
def tearDown(self) -> None: # 調用tearDown
super().tearDown()
print("測試用例執行後操作")
if __name__ == '__main__':
unittest.main()
(idt_dev) ➜ ui_auto_test python unittest_demo.py
測試用例執行前操作
num02
測試用例執行後操作
.測試用例執行前操作
num03
測試用例執行後操作
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
setUp 和 tearDown 會在每一個測試用例執行時生效,setUp在用例執行前執行,tearDown在用戶執行後執行。
全局測試用例
在所有測試用例執行前可能也會需要一些初始化工作,如果能完成就可以避免在單個用例中重複工作。而 setUpClass 和 tearDownClass 可以完成相關工作。
- setUpClass 初始化測試環境且只會執行一次。在類中需要加上@classmethod
- tearDownClass 恢復測試環境且只會執行一次。在類中需要加上@classmethod
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
print("測試前的操作")
@classmethod
def tearDownClass(cls) -> None:
super().tearDownClass()
print("測試後的操作")
def test_add_01(self):
print("num01")
def test_add_02(self):
print("num02")
if __name__ == '__main__':
unittest.main()
測試前的操作
num01
.num02
.測試後的操作
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
在所有測試用例之前會執行setUpClass,而且只會執行一次。所有測試用例執行完成之後執行tearDownClass,同樣只會執行一次。
斷言表達式
斷言表達式用於檢測一個變量是否符合預期,常見的斷言表達式如下:
斷言表達式
示例如下:
import unittest
class Test(unittest.TestCase):
def setUp(self) -> None:
super().setUp()
print("測試開始")
def test_add_01(self):
print("1 == 1")
self.assertEqual(1, 1) # 成功
def test_add_02(self):
print("1 == 2")
self.assertEqual(1, 2) # 失敗
def test_add_03(self):
print("x is True")
self.assertTrue(True) # 成功
def test_add_04(self):
print("x is False")
self.assertFalse(False) # 失敗
def test_add_05(self):
print("a in b")
self.assertIn(1, [1,2,3]) # 成功
def test_add_06(self):
print("a not in b")
self.assertNotIn(1, [1,2,3]) # 失敗
def tearDown(self) -> None:
super().tearDown()
print("測試結束")
if __name__ == '__main__':
unittest.main()
測試開始
1 == 1
測試結束
.測試開始
1 == 2
測試結束
F測試開始
x is True
測試結束
.測試開始
x is False
測試結束
.測試開始
a in b
測試結束
.測試開始
a not in b
測試結束
F
======================================================================
FAIL: test_add_02 (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unittest_demo.py", line 15, in test_add_02
self.assertEqual(1, 2) # 失敗
AssertionError: 1 != 2
======================================================================
FAIL: test_add_06 (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unittest_demo.py", line 31, in test_add_06
self.assertNotIn(1, [1,2,3]) # 失敗
AssertionError: 1 unexpectedly found in [1, 2, 3]
----------------------------------------------------------------------
Ran 6 tests in 0.000s
FAILED (failures=2)
測試工程
單元測試是一個工程,由多個python文件組成的,工程的代碼組織應該有一個清晰的規範。規範如下:
- 創建一個包保存所有的文件
- 一個主入口執行所有單元測試
結構如下:
test_1.py
import unittest
class Test(unittest.TestCase):
def setUp(self) -> None:
super().setUp()
print("測試開始")
def test_add_01(self):
print("1 == 1")
self.assertEqual(1, 1)
def test_add_02(self):
print("1 == 2")
self.assertEqual(1, 2)
def tearDown(self) -> None:
super().tearDown()
print("測試結束")
test_2.py
import unittest
class Test(unittest.TestCase):
def setUp(self) -> None:
super().setUp()
print("測試開始")
def test_add_03(self):
print("x is True")
self.assertTrue(True)
def test_add_04(self):
print("x is False")
self.assertFalse(False)
def tearDown(self) -> None:
super().tearDown()
print("測試結束")
test_main.py
import unittest
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_demo", "test*.py")
# 2.TextTestRunner運行用例
runer = unittest.TextTestRunner()
runer.run(suite)
運行結果:
測試開始
1 == 1
測試結束
.測試開始
1 == 2
測試結束
F測試開始
x is True
測試結束
.測試開始
x is False
測試結束
.
======================================================================
FAIL: test_add_02 (test_1.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/ljk/Documents/code/python-dev/ui_auto_test/unittest_demo/test_1.py", line 15, in test_add_02
self.assertEqual(1, 2)
AssertionError: 1 != 2
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures=1)
selenium + unittest 完整示例
import time
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
class MyTestCase(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome() # 初始化瀏覽器驅動
def test_baidu_search(self):
# 打開百度首頁
self.driver.get("https://www.baidu.com")
# 斷言頁面標題是否包含"百度一下"
self.assertIn("百度一下", self.driver.title)
# 找到搜索框並填充內容
search_input = self.driver.find_element(By.NAME, "wd")
search_query = "python selenium"
search_input.send_keys(search_query)
time.sleep(6)
search_input.submit()
self.driver.implicitly_wait(5)
# 定位搜索結果元素
search_results = self.driver.find_elements(By.CSS_SELECTOR, ".result.c-container")
for result in search_results:
print(result.text)
# 斷言搜索結果是否顯示
self.assertTrue(search_results)
def tearDown(self):
self.driver.quit() # 關閉瀏覽器驅動
if __name__ == '__main__':
unittest.main()