程式碼測試用例指南

weixin_34138377發表於2018-12-28

將測試程式碼和執行程式碼一起寫是一種非常好的習慣。聰明地使用這種方法將會幫助你更加精確地定義程式碼的含義,並且程式碼的耦合性更低。

測試的通用規則:

  • 測試單元應該集中於小部分的功能,並且證明它是對的。
  • 每個測試單元應該完全獨立。
  • 通過Mock去除依賴
  • 儘量使測試單元快速執行。
  • 實現鉤子來持續整合

我們通過一個簡單的python程式及unittest作為示例來為大家介紹如何進行測試,這裡推薦大家使用python3來執行示例。

我們先建立一個將會使用的測試目錄

mkdir /tmp/TestHookTest
cd /tmp/TestHookTest

測試單元應該集中於小部分的功能,並且證明它是對的

下圖為unittest包中包含的斷言

32161-52f09251176c9cb6.jpg
image

我們現在來寫一個通過使用者名稱獲得github資訊的一個函式,並對這個函式進行測試

# test.py
import unittest
import json

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

通過python3 -m unittest test執行

每個測試單元應該完全獨立

32161-820568638c17e10b.jpg
image
  • 每個都能夠單獨執行,除了呼叫的命令,都需在測試套件中。要想實現這個規則,測試單元應該載入最新的資料集,之後再做一些清理。

  • 如果有資料庫依賴,在每次測試前建立測試資料庫,結束後銷燬該資料庫,測試應該有單獨的資料庫,不要在生產和開發環境測試,避免資料變化引起的測試失敗

  • 通過Mock去除依賴

假設我們現在想把取得的使用者資料儲存到本地,並測試是否正確儲存

# test.py
import unittest
import json

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

def save_data(data):
    with open('data.json', 'w') as f:
        f.write(json.dumps(data))

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

    def test_save_data(self):
        data = fetch_github_profile('ZhangBohan')
        save_data(data)

        with open('data.json') as f:
            file_data = json.loads(f.read())
            self.assertIsNotNone(file_data)
            self.assertEqual(data['id'], file_data['id'])

在這個測試中我們的test_save_data中的data依賴fetch_github_profile中的返回資料,現實情況中會遇到更為複雜的依賴,為了一個測試用例,我們可能需要構建大量的初始化資料。我們可以通過mock來解除這個依賴,讓test_save_data專注於測試儲存資料部分

# test.py
import unittest
import json
from unittest.mock import MagicMock

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

def save_data(data):
    with open('data.json', 'w') as f:
        f.write(json.dumps(data))


FAKE_PROFILE_DATA = {
  "login": "ZhangBohan",
  "id": 2317407
}

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

    def test_save_data(self):
        fetch_github_profile = MagicMock(return_value=FAKE_PROFILE_DATA)
        data = fetch_github_profile('ZhangBohan')
        save_data(data)

        with open('data.json') as f:
            file_data = json.loads(f.read())
            self.assertIsNotNone(file_data)
            self.assertEqual(data['id'], file_data['id'])

儘量使測試單元快速執行

32161-606ca6049bfa1eb8.png
image

如果一個單獨的測試單元需要較長的時間去執行,開發進度將會延遲,測試單元將不能如期常態性執行。有時候,因為測試單元需要複雜的資料結構,並且當它執行時每次都要載入,所以其執行時間較長。把執行吃力的測試單元放在單獨的測試元件中,並且按照需要執行其它測試單元。

實現hook來持續整合

通過程式碼提交的本地hook或者webhook來持續整合測試你的程式碼。

舉個git本地hook的例子(這可假設你瞭解git hook的工作原理)。

> git init
> vim .git/hooks/pre-commit

.git/hooks/pre-commit檔案中寫入

#!/bin/sh

cd /tmp/TestHookTest && python3 -m unittest test

執行:

> chmod +x .git/hooks/pre-commit

> git add test.py
> git commit -m "test hook"
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
[master (root-commit) b390117] test hook
 1 file changed, 9 insertions(+)
 create mode 100644 test.py

在遠端程式碼倉庫部署的webhook能更好的測試全部程式碼。

Python指南-測試你的程式碼

相關文章