1. 程式人生 > >你會寫單元測試嗎

你會寫單元測試嗎

關於我
一個有思想的程式猿,終身學習實踐者,目前在一個創業團隊任team lead,技術棧涉及Android、Python、Java和Go,這個也是我們團隊的主要技術棧。
Github:https://github.com/hylinux1024
微信公眾號:終身開發者(angrycode)

也許你已經聽說過Test Driven Development,但不知道你是否遵循這個規則呢?其實我自己在寫程式碼的時候也很少會先寫單元測試再寫業務功能邏輯。這不我也今天也來學習如何在Python中寫單元測試。

0x00 unittest

Python中的unittest單元測試框架跟其它語言如JUnit是類似的。它支援測試自動化、配置共享以及關機程式碼測試。

假設在我的專案目錄下有一個mysum模組用於計算列表中各個數之和。
還有一個test_mysum.py用於編寫單元測試的檔案。

myproject/
│
├── mysum/
│   └── __init__.py
└── unittests
    └── test_mysum.py

開啟mysum模組中的__init__.py檔案。

新增下面的方法

def sum(args):
    total = 0
    for arg in args:
        total += arg
    return total

開啟test_mysum.py,編寫單元測試

import unittest

from mysum import sum

class TestSum(unittest.TestCase):
    def test_list_int(self):
        """
        測試一個整數列表的和
        """
        data = [1, 2, 3]
        result = sum(data)
        self.assertEqual(result, 6)


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

首先匯入我們要測試的模組mysum
測試用例類通過繼承unittest.TestCase來實現,測試方法test_list_int是以test開頭的。
在這個方法中定義了一個整型列表,執行sum方法,然後判斷執行結果是否與預期相符。

最後呼叫unittest.main()來執行這個測試用例

Ran 1 test in 0.001s

OK

如果再新增一個方法

def test_list_sum(self):
    data = [1, 3, 4]

    result = sum(data)
    self.assertEqual(result, 6)

執行後會看到如下類似資訊

6 != 8

Expected :8
Actual   :6

從這個輸出資訊可以看出期望值與實際值不相符,這時候如果我們的測試用例沒有問題,那就要看看mysum的實現邏輯了。

從這個例子中可以總結一個測試用例的過程:

  1. 構建輸入資料
  2. 執行要測試模組,獲取執行結果
  3. 與預期結果相比較,根據結果修改程式碼

0x01 setup/tearDown

在編寫單元測試時,還可以重寫父類的setuptearDown方法,可以在執行測試邏輯開始前和結束時做一些處理。例如在setup方法中可以初始化測試資料,在tearDown方法做一些清理工作。

import unittest

class TestBasic(unittest.TestCase):
    def setUp(self):
        # 載入測試資料
        self.app = App(database='fixtures/test_basic.json')

    def test_customer_count(self):
        self.assertEqual(len(self.app.customers), 100)

    def test_existence_of_customer(self):
        customer = self.app.get_customer(id=10)
        self.assertEqual(customer.name, "Org XYZ")
        self.assertEqual(customer.address, "10 Red Road, Reading")
    
    def tearDown(self):
        self.app.releaseDB()

在每個測試執行時setuptearDown都會被執行一次。

0x02 pytest

pytest是一個第三方測試框架,使用它不需要繼承某個類,它可以使用原生的assert語句用於測試結果的斷言。

它的用法也很簡單

首先通過pip安裝

➜ pip install pytest

我們寫一個單獨的tests資料夾下建立測試用例檔案test_pytest.py
注意:這裡pytest的檔案必須與上文的unittests檔案必須區分開,否則會出現ModuleNotFoundError。我已經在這裡踩坑。

我這裡使用pytest單元測試的檔案結構為

tests
│
└── test_func.py

test_func.py的內容為

# 匯入我們要測試的模組
from mysum import sum


def test_answer():
    data = [1, 2, 3]

    assert sum(data) == 5

測試方法以test開頭

然後再命令列中執行

➜ python -m pytest tests/test_func.py 

注意:這裡要使用python -m pytest,如果直接使用pytest會提示ModuleNotFoundError: No module named 'mysum'

執行結果如下

tests/test_func.py F                                                                                                                                                                          [100%]

======================================================= FAILURES ==============================================
_______________________________________________________ test_answer ___________________________________________

    def test_answer():
        data = [1, 2, 3]
    
>       assert sum(data) == 5
E       assert 6 == 5
E        +  where 6 = sum([1, 2, 3])

tests/test_func.py:11: AssertionError
=======================================================1 failed in 0.03 seconds =======================================================

由於6!=5,這個單元測試提示了出錯的位置。

0x03 總結一下

測試程式碼對編寫程式碼非常重要,寫單元測試也是一個好習慣。本文也只是一個開胃菜。要寫出健壯的程式碼,從寫單元測試開始吧。

0x04 學習資料

  • https://docs.python.org/3/library/unittest.html
    Unit testing framework
  • https://realpython.com/python-testing/
    Getting Started With Testing in Python
  • https://docs.python-guide.org/writing/tests/
    Testing Your Code