前一篇講到了 TDD 測試驅動開發的相關概念和環境搭建,這篇就着手開始用TDD方式開發了。首先這篇須要編寫用戶相關的API接口,如用戶註冊、用戶登陸、用戶驗證等功能。python
這裏使用 Python 自帶的測試框架 unittest 來編寫簡單測試,後續會使用更好用的 pytest 框架來完成整個網站的測試。
首先,最基本的測試是 Flask 應用實例是否存在,新建一個 test_basic.py 文件,當前 v2ex 項目結構以下:git
├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md └── tests ├── test_basic.py └── test_user.py
編寫 test_user.py 代碼以下:github
import unittest from flask import current_app from server import create_app class UserTestCase(unittest.TestCase): def setUp(self): self.app = create_app('testing') self.app_context = self.app.app_context() self.app_context.push() def tearDown(self): self.app_context.pop() def test_app_is_exist(): """測試 Flask 實例是否存在""" self.assertFalse(current_app is None) if __name__ == '__main__': unittest.main()
這裏使用 unittest 編寫了一個測試類,setUp() 和 tearDown() 容許執行每一個測試用例前分別初始化和清理測試環境,setUp 可用於建立應用實例,而後測試完成後就經過 tearDown 函數清理.flask
很明顯,這個測試確定是失敗的,由於很明顯app
from server import create_app
這一行是什麼東東啊,好像項目裏沒有存在啊,這就是 TDD 的一個概念了,先編寫一個預料之中的失敗,而後一步步的把失敗那部分改進到測試成功爲止。
框架
新建一個 server 包,用於保存 v2ex 的業務邏輯代碼,下面是當前項目的結構:函數
v2ex ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── server │ └── __init__.py └── tests ├── test_basic.py └── test_user.py
爲了組織好模塊,會將多個模塊分爲包。簡單來講,包就是文件夾,但該文件夾下必須存在 __init__.py
文件, 而後能夠在 __init__.py
文件中新增建立 Flask 實例的工廠函數,代碼以下所示:單元測試
from flask import Flask from config import config def create_app(config_name: str): """ 工廠函數,用於延遲建立 Flask 實例,可用於建立多個實例. :param config_name: 配置名稱,可根據開發環境、測試環境、生產環境區分 :return: Flask 示例 """ app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app) return app
而後運行 tests 文件夾下的 test_basic.py 文件,測試
$ python3 tests/test_basic.py Ran 1 test in 0.007s FAILED (errors=1) Error Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 59, in testPartExecutor yield File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 611, in run self.setUp() File "/Users/guoweikuang/project/github/v2ex/tests/test_basic.py", line 14, in setUp self.app = create_app('testing') File "/Users/guoweikuang/project/github/v2ex/server/__init__.py", line 17, in create_app app.config.from_object(config[config_name]) NameError: name 'config' is not defined
這還不能成功,由於咱們從最開始就說過,要使用最佳實踐方式來一步步實現網站,所以實例化 Flask 須要單獨存在一個配置模塊(config.py),目的就是把配置和其它功能區分開。
並且 TDD 的思想就是每次編寫最少許的代碼取得一些進展,再運行測試,如此不斷重複,直到測試成功爲止,最後可能還要重構代碼,測試能保證不破壞任何一個功能。網站
當前文件結構以下:
v2ex ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── config.py ├── server │ └── __init__.py └── tests ├── test_basic.py └── test_user.py
前面一節已經知道測試失敗緣由,這一節就編寫一個config.py 模塊使單元測試經過:
class Config(object): """ 配置基類,全部其它配置類都要繼承該類. """ @staticmethod def init_app(app): pass class DevelopmentConfig(Config): """ 開發環境配置 """ DEBUG = True class ProductionConfig(Config): """ 生產環境配置 """ DEBUG = False class TestingConfig(Config): """ 測試環境配置 """ TESTING = True DEBUG = True config = { 'development': DevelopmentConfig, 'product': ProductionConfig, 'testing': TestingConfig, 'default': DevelopmentConfig, }
使用最少的代碼使測試成功,而後運行 test_basic.py 看看測試是否經過
$ python tests/test_basic.py . ---------------------------------------------------------------------- Ran 1 test in 0.001s OK
從上面步驟能夠總結到,先寫測試代碼,而後設法使測試一步步經過的 TDD 開發方式是十分有用的,保證了開發的功能符合預期的想法。當功能愈來愈複雜的時候,你可能修改了一些東西,致使另一個功能的不可用,而單元測試也能保證了原有功能被破壞後能被檢測出來。這裏的代碼均可以到 v2ex項目中查看,每一篇文章都對應着一個 tag, 想要查看每篇文章的修改能夠切到對應標籤上查看。