問題背景
在寫一些工具類的時候吕喘,需要將實例化的django Model(大致上可以理解為表格中的數(shù)據(jù)行)作為參數(shù)傳入胳挎,原生的unittest在VSCode上的支持上感覺不是很好,就嘗試了一下pytest前酿。挺好用的杠河,同時還發(fā)現(xiàn)了它還支持一個更便捷的方式郭毕,編寫大量檢驗邏輯相同的單元測試案例,讓最近被單元測試困擾的我頓時醍醐灌頂般驚醒刘急。代碼范例如下(案例一二與django相關(guān)棚菊,二更加通用,三為批量寫單元測試的案例):
注:以下的一些描述僅方便個人理解叔汁,如有錯誤歡迎評論區(qū)指正统求。
案例一: django混入真實的數(shù)據(jù)記錄
fixture大致可以理解為在某一個作用域/階段[scope]預(yù)執(zhí)行的一些函數(shù),一般的fixture只要寫好function定義就會在恰當(dāng)?shù)臅r機被調(diào)用
其中作用于function yield類的fixture似乎需要將定義好的function傳入test_function的參數(shù)才會被啟用据块,同時可以將yield的object傳給test_function码邻,
而且可以做到類似unittest的setUp/tearDown的作用——在yield之前的命令將會預(yù)先執(zhí)行(setUp),yield之后的命令則在test_function結(jié)束后執(zhí)行(tearDown)
import pytest
from .models import MyModel
@pytest.fixture(scope="class")
def django_db_setup(): # The name must be django_db_setup
pass
@pytest.fixture(scope="function")
def real_record():
# Start setting up for a test function
# similar-to setUp for scope="function" when real_record is passed as a parameter to a test function
record = MyModel.objects.get(id=1)
# end setting up for function
yield record
# Start tearing down for a test function
# similar-to tearDown
pass # do something afterwards
class TestRealRecord:
@pytest.mark.django_db
def test_real_record(self, real_record):
assert real_record.id == 1
案例二: django混入虛擬的數(shù)據(jù)記錄(實際上就是混入一個實例化的class)
如果不想連接數(shù)據(jù)庫瑰钮,只是寫一些虛擬的參數(shù)冒滩,但同時還想要保留Model的其他方法可以在測試時被調(diào)用,可以直接使用function yield fixture將實例化的Model傳入test function
import pytest
from .models import MyModel
@pytest.fixture(scope="function")
def simulated_record():
record = MyModel(id=9527)
yield record
class TestSimulatedRecord:
def test_simulated_record(self, simulated_record):
assert simulated_record.id == 9527
案例三浪谴、批量生成測試
利用pytest.mark.parametrize
可以對同一類檢驗邏輯的測試案例快速編寫單元測試开睡,可以被VSCode識別為折疊后的一組測試,展開折疊后還可以對批量生成的單個測試案例進行調(diào)試苟耻。解鎖快速生成單元測試的技能篇恒,讓TDD(Test Driven Development)更加便捷。
以下每一個tuple()內(nèi)的一組變量將被傳入test function作為一個測試案例凶杖,所有的幾個測試案例將會被分為一組折疊到VSCode的Test界面里胁艰。
import pytest
class TestBatchRecord:
@pytest.mark.parametrize(
'expression, answer, correct',
[
("1+1", 2, True),
("2+2", 3, False),
]
)
def test_parametrize(self, expression, answer, correct):
if correct:
assert eval(expression) == answer
else:
assert eval(expression) != answer
案例四、數(shù)據(jù)庫只讀測試
直接在開發(fā)機器的數(shù)據(jù)庫中進行測試智蝠,可以結(jié)合已有的真實數(shù)據(jù)進行相對準確的讀取相關(guān)的測試
import pytest
@pytest.fixture
def django_db_setup(django_db_blocker):
from django.conf import settings
settings.DATABASES['default']['NAME'] = 'db-name-in-develop-machine'
def TestTasksCollection:
@pytest.mark.django_db
def test_task_1(self):
pass