天天看點

Python 程式設計必不可少的測試架構「pytest篇」

測試是為了更高效的完成功能實作。

pytest 是基于 unittest 實作的第三方測試架構,比 unittest 更加的簡潔、高效,并且可以完美相容 unittest 的測試代碼,無需對其做任何的修改。

pytest 的使用

使用

pip install pytest

可以直接安裝 pytest 測試架構。

pytest 通過裝飾器「@pytest.fixture」将函數設定為固件,以便于在測試開始前和測試開始後執行相應的操作。在函數中通過 yield 将同一個函數分為兩部分,分别在測試前和測試後執行,避免遺漏資源的釋放。

pytest 通過 conftest.py 檔案進行資料共享,在其它檔案中無需導入即可使用。并且 pytest 會自動識别 conftest.py 檔案,無需顯示指定。可以為子檔案夾單獨設定 conftest.py 檔案。

在 Python 程式設計必不可少的測試架構「unittest 篇」 中講述了 unittest 測試架構的使用,在這裡我們将上一篇中的測試使用 pytest 重新實作,來觀察 unittest 和 pytest 的差別。

我們将所有的公共函數「固件」放入 conftest.py 檔案中,檔案内容大緻如下:

DEFAULT_USERNAME = 'test'
DEFAULT_PASSWORD = 'test'

@pytest.fixture
def app():
    db_fd, db_file = tempfile.mkstemp()
    app = create_app()
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+ db_file

print('pytest start')

with app.app_context():
        db.drop_all()
        db.create_all()
        user = User.create(
            name=DEFAULT_USERNAME,
            password=DEFAULT_PASSWORD,
            permission=Permission.ADMINISTRATOR,
            active=True)

yield app
print('pytest stop')
with app.app_context():
        db.session.remove()
        db.drop_all()
    os.close(db_fd)
    os.unlink(db_file)

@pytest.fixture
def clinet(app):
return app.test_client()

@pytest.fixture
def headers(app, clinet):
    rv = clinet.post('/api/v01/user/login',
                        data=json.dumps(dict(user_name='test', password='test')),
                        content_type='application/json')
    data = json.loads(rv.data)
    token = data['token']
    headers = {"Authorization":"Bearer "+token, 'Content-Type': 'application/json'}

yield headers

pass           

複制

conftest.py 檔案實作的内容實際上就是 unittest 中

setUp

setDown

函數的内容。整體實作上更加的簡單明了。

在測試檔案中可以直接将使用裝飾器

@pytest.fixture

标記的函數以同名參數的方法傳入測試函數中,即可在測試函數中使用相應的功能。同樣以 login 和 add_user 兩個功能的測試為例,實作在 pytest 架構的測試實作:

def test_login(clinet):
    rv = clinet.post('/api/v01/user/login',
                    data=json.dumps(dict(user_name='test', password='test')),
                    content_type='application/json')
    data = json.loads(rv.data)

assert rv.status_code == 200
assert data['status'] == 1
assert data['name'] == 'test'
assert data['token'] isnotNone
assert data['admin'] isnotNone
assert data['expire'] isnotNone

def test_add_user(clinet, headers):

    rv = clinet.post('/api/v01/user',
                    data=json.dumps(dict(user_name='123', password='123', admin=False)),
                    headers=headers)
    data = json.loads(rv.data)
assert rv.status_code == 200
assert data['status'] == 1           

複制

在 pytest 中使用 assert 加表達式的方法來對結果進行驗證,而在 unittest 中要通過 assertEqual、assertIn、assertTrue、assertFalse 等等來完成,要記憶的更多實作也更複雜。

使用

pytest

來運作測試執行個體,可以看到如下結果

================================================================================ test session starts ================================================================================
platform darwin -- Python3.7.5, pytest-5.3.3, py-1.8.1, pluggy-0.13.1
rootdir: ***************
collected 4 items

tests/test_user.py ..                                                                                                                                                         [ 50%]
tests/unittest/test_user_unittest.py ..                                                                                                                                       [100%]           

複制

可以看到測試結果标記了測試進度,并且同步測試了 unittest 的測試用例。你可以通過

-s

參數來顯示測試函數中的 print 輸出内容。

如果你使用

-s

參數來 print 函數的輸出的話,就會看到目前所有的固件「Fixture」在每個測試函數開始和完成時都會執行一次,這不是很浪費資源嗎,是否可以每次測試運作隻執行一次固件呢,答案是可以的,這就要用到固件的作用域了,通過裝飾器

@pytest.fixture(scope='session')

來設定該固件的作用域是整個測試過程。更多内容請看文末的思維導圖。

unittest 和 pytest 的比較

  • 固件「Fixture」 在 unittest 中通過固定的函數 setUp 和 tearDown 來實作測試用例的前置和後置函數,并且是針對所有測試用例的。而在 pytest 中通過裝飾器來設定固件的函數命名方式更加的靈活,并且可以将固件設定為函數級、類級、子產品級、以及全局級。pytest 以 conftest.py 作為預設配置實作全局資料共享。
  • 斷言實作方式 在 unittest 中将每種判斷方式單獨實作了一個斷言函數,比如 assertEqual、assertIn、assertTrue、assertFalse 等等,使用起來過于麻煩。在 pytest 中直接使用 assert + 表達式的方法來實作,更加清晰明了。
  • 參數化 unittest 本身沒有實作參數化的功能,pytest 可以通過裝飾器

    @pytest.mark.parametrize

    快速實作參數化。

pytest 知識點的思維導圖:

Python 程式設計必不可少的測試架構「pytest篇」

公衆号回複 Flask 擷取相關源碼!