在用 pytest 寫測試時,可能有些程式碼每個測試一開始都需要執行,像是建立資料庫連線:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import mysql.connector
def test_read_db(): # mydb 在兩個 test 裡都有出現 mydb = mysql.connector.connect( host="localhost", user="yourusername", password="yourpassword" ) mycursor = mydb.cursor() mycursor.execute("SELECT * FROM customers WHERE name = 'Andy'") rows = mycursor.fetchall() assert len(rows) == 1
def test_update_db(): # mydb 在兩個 test 裡都有出現 mydb = mysql.connector.connect( host="localhost", user="yourusername", password="yourpassword" ) mycursor = mydb.cursor() sql = "UPDATE customers SET address = 'Canyon 123' WHERE address = 'Valley 345'" mycursor.execute(sql) mydb.commit() assert mycursor.rowcount == 1
|
這些重複的程式碼如果只需要寫一次的話,想必可以讓你的 code 讀起來較輕鬆,日後維護也比較方便。
這時可以考慮用 pytest 的 fixture 功能來避免程式碼重複。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import pytest import mysql.connector
@pytest.fixture def mydb(): return mysql.connector.connect( host="localhost", user="yourusername", password="yourpassword" )
def test_read_db(mydb): mycursor = mydb.cursor() mycursor.execute("SELECT * FROM customers WHERE name = 'Andy'") rows = mycursor.fetchall() assert len(rows) == 1
def test_update_db(mydb): mycursor = mydb.cursor() sql = "UPDATE customers SET address = 'Canyon 123' WHERE address = 'Valley 345'" mycursor.execute(sql) mydb.commit() assert mycursor.rowcount == 1
|
在上面的 code 裡,你可以發現我把原本的 mydb
轉成以下程式碼
1 2 3 4 5 6 7
| @pytest.fixture def mydb(): return mysql.connector.connect( host="localhost", user="yourusername", password="yourpassword" )
|
然後將 mydb
當作參數傳給 test function,這樣 test function 還是可以使用 mydb
,且 mydb
只要建立一次就可以。
不過由於 MySQL 建立連線需要一段時間,如果每跑一個測試就重建一次連線的話,非常耗時。為了節省時間,我們可以設定 fixture 在所有測試開始前,只執行一次:
1 2 3
| @pytest.fixture(scope="module") def mydb(): ...
|
fixture 有 scope 參數可設定,當設為 "module"
時,fixture 會在 module 內最後一個 test 執行完才被拆掉。
詳細的 fixture scope 參數可以查看這篇文章。