Avage Pytesti täielik potentsiaal täiustatud fikstuuritehnikatega. Õppige kasutama parameetritega testimist ja mock-integratsiooni robustseks ja tõhusaks Pythoni testimiseks.
Pytesti täiustatud fikstuuride valdamine: parameetritega testimine ja mock-integratsioon
Pytest on võimas ja paindlik testimisraamistik Pythoni jaoks. Selle lihtsus ja laiendatavus teevad sellest arendajate lemmiku üle maailma. Üks Pytesti kõige köitvamaid omadusi on selle fikstuuride süsteem, mis võimaldab elegantseid ja korduvkasutatavaid testiseadistusi. See blogipostitus süveneb täiustatud fikstuuritehnikatesse, keskendudes spetsiifiliselt parameetritega testimisele ja mock-integratsioonile. Uurime, kuidas need tehnikad saavad teie testimise töövoogu oluliselt täiustada, viies robustsema ja hooldatavama koodini.
Pytesti fikstuuride mõistmine
Enne täiustatud teemadesse sukeldumist kordame lühidalt üle Pytesti fikstuuride põhitõed. Fikstuur on funktsioon, mis käivitatakse enne iga testfunktsiooni, millele see on rakendatud. Seda kasutatakse testidele kindla algseisundi pakkumiseks, tagades järjepidevuse ja vähendades korduvat koodi. Fikstuurid saavad täita ülesandeid nagu:
- AndmebaasiĂĽhenduse seadistamine
- Ajutiste failide või kataloogide loomine
- Objektide initsialiseerimine spetsiifiliste konfiguratsioonidega
- API-ga autentimine
Fikstuurid soodustavad koodi korduvkasutust ja muudavad teie testid loetavamaks ja hooldatavamaks. Neid saab defineerida erinevates skoobi (funktsioon, moodul, seanss) ulatuses, et kontrollida nende eluiga ja ressursside tarbimist.
Fikstuuri põhinäide
Siin on lihtne näide Pytesti fikstuurist, mis loob ajutise kataloogi:
import pytest
import tempfile
import os
@pytest.fixture
def temp_dir():
with tempfile.TemporaryDirectory() as tmpdir:
yield tmpdir
Selle fikstuuri kasutamiseks testis lisage see lihtsalt oma testfunktsiooni argumendina:
def test_create_file(temp_dir):
filepath = os.path.join(temp_dir, "test_file.txt")
with open(filepath, "w") as f:
f.write("Hello, world!")
assert os.path.exists(filepath)
Parameetritega testimine Pytestiga
Parameetritega testimine võimaldab teil sama testfunktsiooni käivitada mitu korda erinevate sisendandmete komplektidega. See on eriti kasulik funktsioonide testimiseks, millel on erinevad sisendid ja oodatavad väljundid. Pytest pakub @pytest.mark.parametrize dekoraatorit parameetritega testide rakendamiseks.
Parameetritega testimise eelised
- Vähendab koodi dubleerimist: Vältige mitme peaaegu identse testfunktsiooni kirjutamist.
- Parandab testide katvust: Testige hõlpsalt laiemat valikut sisendväärtusi.
- Suurendab testide loetavust: Määratlege selgelt iga testjuhtumi sisendväärtused ja oodatavad väljundid.
Parameetritega testimise põhinäide
Oletame, et teil on funktsioon, mis liidab kaks arvu:
def add(x, y):
return x + y
Saate kasutada parameetritega testimist selle funktsiooni testimiseks erinevate sisendväärtustega:
import pytest
@pytest.mark.parametrize("x, y, expected", [
(1, 2, 3),
(5, 5, 10),
(-1, 1, 0),
(0, 0, 0),
])
def test_add(x, y, expected):
assert add(x, y) == expected
Selles näites määratleb @pytest.mark.parametrize dekoraator neli testjuhtumit, millest igaühel on erinevad väärtused x, y ja oodatava tulemuse jaoks. Pytest käivitab test_add funktsiooni neli korda, üks kord iga parameetrite komplekti jaoks.
Täiustatud parameetritega testimise tehnikad
Pytest pakub mitmeid täiustatud tehnikaid parameetritega testimiseks, sealhulgas:
- Fikstuuride kasutamine parameetritega: Kombineerige fikstuure parameetritega, et pakkuda iga testjuhtumi jaoks erinevaid seadistusi.
- Testjuhtumite ID-d: Määrake testjuhtumitele kohandatud ID-d parema aruandluse ja silumise jaoks.
- Kaudne parameetritega testimine: Parameetrite määramine fikstuuridele edastatavatele argumentidele, võimaldades dünaamilist fikstuuride loomist.
Fikstuuride kasutamine parameetritega
See võimaldab teil fikstuure dünaamiliselt konfigureerida testile edastatud parameetrite põhjal. Kujutage ette, et testite funktsiooni, mis suhtleb andmebaasiga. Võiksite kasutada erinevate testjuhtumite jaoks erinevaid andmebaasi konfiguratsioone (nt erinevaid ühendusstringe).
import pytest
@pytest.fixture
def db_config(request):
if request.param == "prod":
return {"host": "prod.example.com", "port": 5432}
elif request.param == "test":
return {"host": "test.example.com", "port": 5433}
else:
raise ValueError("Invalid database environment")
@pytest.fixture
def db_connection(db_config):
# Simulate establishing a database connection
print(f"Connecting to database at {db_config['host']}:{db_config['port']}")
return f"Connection to {db_config['host']}"
@pytest.mark.parametrize("db_config", ["prod", "test"], indirect=True)
def test_database_interaction(db_connection):
# Your test logic here, using the db_connection fixture
print(f"Using connection: {db_connection}")
assert "Connection" in db_connection
Selles näites on db_config fikstuur parameetritega. indirect=True argument ütleb Pytestile, et see edastaks parameetrid ("prod" ja "test") db_config fikstuuri funktsioonile. db_config fikstuur tagastab seejärel erinevad andmebaasi konfiguratsioonid sõltuvalt parameetri väärtusest. db_connection fikstuur kasutab db_config fikstuuri andmebaasiühenduse loomiseks. Lõpuks kasutab test_database_interaction funktsioon db_connection fikstuuri andmebaasiga suhtlemiseks.
Testjuhtumite ID-d
Kohandatud ID-d pakuvad teie testjuhtumitele testiaruandes kirjeldavamaid nimesid, muutes vigade tuvastamise ja silumise lihtsamaks.
import pytest
@pytest.mark.parametrize(
"input_string, expected_output",
[
("hello", "HELLO"),
("world", "WORLD"),
("", ""),
],
ids=["lowercase_hello", "lowercase_world", "empty_string"],
)
def test_uppercase(input_string, expected_output):
assert input_string.upper() == expected_output
Ilma ID-deta genereeriks Pytest üldised nimed nagu test_uppercase[0], test_uppercase[1] jne. ID-dega kuvab testiaruanne tähendusrikkamaid nimesid nagu test_uppercase[lowercase_hello].
Kaudne parameetritega testimine
Kaudne parameetritega testimine võimaldab teil parameetrid määrata fikstuuri sisendile, mitte otse testfunktsioonile. See on kasulik, kui soovite luua erinevaid fikstuuri eksemplare parameetri väärtuse põhjal.
import pytest
@pytest.fixture
def input_data(request):
if request.param == "valid":
return {"name": "John Doe", "email": "john.doe@example.com"}
elif request.param == "invalid":
return {"name": "", "email": "invalid-email"}
else:
raise ValueError("Invalid input data type")
def validate_data(data):
if not data["name"]:
return False, "Name cannot be empty"
if "@" not in data["email"]:
return False, "Invalid email address"
return True, "Valid data"
@pytest.mark.parametrize("input_data", ["valid", "invalid"], indirect=True)
def test_validate_data(input_data):
is_valid, message = validate_data(input_data)
if input_data == {"name": "John Doe", "email": "john.doe@example.com"}:
assert is_valid is True
assert message == "Valid data"
else:
assert is_valid is False
assert message in ["Name cannot be empty", "Invalid email address"]
Selles näites on input_data fikstuur parameetritega väärtustega "valid" ja "invalid". indirect=True argument ütleb Pytestile, et see edastaks need väärtused input_data fikstuuri funktsioonile. input_data fikstuur tagastab seejärel erinevad andmesõnastikud parameetri väärtuse põhjal. test_validate_data funktsioon kasutab seejärel input_data fikstuuri, et testida validate_data funktsiooni erinevate sisendandmetega.
Mockimine Pytestiga
Mockimine on tehnika, mida kasutatakse reaalsete sõltuvuste asendamiseks kontrollitud asendajatega (mockidega) testimise ajal. See võimaldab teil testitavat koodi isoleerida ja vältida tuginemist välistele süsteemidele, nagu andmebaasid, API-d või failisüsteemid.
Mockimise eelised
- Isoleerige kood: Testige koodi eraldiseisvalt, ilma välistele sõltuvustele tuginemata.
- Kontrollige käitumist: Määratlege sõltuvuste käitumine, nagu tagastusväärtused ja erandid.
- Kiirendage teste: Vältige aeglaseid või ebausaldusväärseid väliseid süsteeme.
- Testige äärmuslikke juhtumeid: Simuleerige veaolukordi ja äärmuslikke juhtumeid, mida on reaalses keskkonnas raske reprodutseerida.
unittest.mock teegi kasutamine
Python pakub unittest.mock teeki mockide loomiseks. Pytest integreerub sujuvalt unittest.mock-iga, muutes sõltuvuste mockimise teie testides lihtsaks.
Mockimise põhinäide
Oletame, et teil on funktsioon, mis hangib andmeid välisest API-st:
import requests
def get_data_from_api(url):
response = requests.get(url)
response.raise_for_status() # Raise an exception for bad status codes
return response.json()
Selle funktsiooni testimiseks ilma tegelikult API-le päringut tegemata saate mockida requests.get funktsiooni:
import pytest
import requests
from unittest.mock import patch
@patch("requests.get")
def test_get_data_from_api(mock_get):
# Configure the mock to return a specific response
mock_get.return_value.json.return_value = {"data": "test data"}
mock_get.return_value.status_code = 200
# Call the function being tested
data = get_data_from_api("https://example.com/api")
# Assert that the mock was called with the correct URL
mock_get.assert_called_once_with("https://example.com/api")
# Assert that the function returned the expected data
assert data == {"data": "test data"}
Selles näites asendab @patch("requests.get") dekoraator requests.get funktsiooni mock-objektiga. mock_get argument on mock-objekt. Seejärel saame konfigureerida mock-objekti tagastama spetsiifilise vastuse ja kinnitama, et seda kutsuti õige URL-iga.
Mockimine fikstuuridega
Saate ka fikstuure kasutada mockide loomiseks ja haldamiseks. See võib olla kasulik mockide jagamiseks mitme testi vahel või keerukamate mock-seadistuste loomiseks.
import pytest
import requests
from unittest.mock import Mock
@pytest.fixture
def mock_api_get():
mock = Mock()
mock.return_value.json.return_value = {"data": "test data"}
mock.return_value.status_code = 200
return mock
@pytest.fixture
def patched_get(mock_api_get, monkeypatch):
monkeypatch.setattr(requests, "get", mock_api_get)
return mock_api_get
def test_get_data_from_api(patched_get):
# Call the function being tested
data = get_data_from_api("https://example.com/api")
# Assert that the mock was called with the correct URL
patched_get.assert_called_once_with("https://example.com/api")
# Assert that the function returned the expected data
assert data == {"data": "test data"}
Siin loob mock_api_get mocki ja tagastab selle. Seejärel kasutab patched_get monkeypatch-i, mis on pytesti fikstuur, et asendada reaalne `requests.get` mockiga. See võimaldab teistel testidel kasutada sama mockitud API lõpp-punkti.
Täiustatud mockimise tehnikad
Pytest ja unittest.mock pakuvad mitmeid täiustatud mockimise tehnikaid, sealhulgas:
- Kõrvalmõjud: Määratlege mockide kohandatud käitumine sisendargumentide põhjal.
- Atribuutide mockimine: Mockige objektide atribuute.
- Kontekstihaldurid: Kasutage mocke kontekstihaldurites ajutiste asenduste jaoks.
Kõrvalmõjud
Kõrvalmõjud võimaldavad teil määratleda oma mockidele kohandatud käitumise, mis põhineb nende saadavatel sisendargumentidel. See on kasulik erinevate stsenaariumide või veaolukordade simuleerimiseks.
import pytest
from unittest.mock import Mock
def test_side_effect():
mock = Mock()
mock.side_effect = [1, 2, 3]
assert mock() == 1
assert mock() == 2
assert mock() == 3
with pytest.raises(StopIteration):
mock()
See mock tagastab järjestikustel kutsetel 1, 2 ja 3, seejärel tõstatab `StopIteration` erandi, kui loend on ammendatud.
Atribuutide mockimine
Atribuutide mockimine võimaldab teil mockida objektide atribuutide käitumist. See on kasulik koodi testimiseks, mis tugineb pigem objekti atribuutidele kui meetoditele.
import pytest
from unittest.mock import patch
class MyClass:
@property
def my_property(self):
return "original value"
def test_property_mocking():
obj = MyClass()
with patch.object(obj, "my_property", new_callable=pytest.PropertyMock) as mock_property:
mock_property.return_value = "mocked value"
assert obj.my_property == "mocked value"
See näide mockib MyClass objekti my_property atribuuti, võimaldades teil kontrollida selle tagastusväärtust testi ajal.
Kontekstihaldurid
Mockide kasutamine kontekstihaldurites võimaldab teil ajutiselt asendada sõltuvusi konkreetse koodiploki jaoks. See on kasulik koodi testimiseks, mis suhtleb väliste süsteemide või ressurssidega, mida tuleks mockida ainult piiratud aja jooksul.
import pytest
from unittest.mock import patch
def test_context_manager_mocking():
with patch("os.path.exists") as mock_exists:
mock_exists.return_value = True
assert os.path.exists("dummy_path") is True
# The mock is automatically reverted after the 'with' block
# Ensure the original function is restored, although we can't really assert
# the real `os.path.exists` function's behavior without a real path.
# The important thing is that the patch is gone after the context.
print("Mock has been removed")
Parameetritega testimise ja mockimise kombineerimine
Neid kahte võimsat tehnikat saab kombineerida, et luua veelgi keerukamaid ja tõhusamaid teste. Saate kasutada parameetritega testimist, et testida erinevaid stsenaariume erinevate mock-konfiguratsioonidega.
import pytest
import requests
from unittest.mock import patch
def get_user_data(user_id):
url = f"https://api.example.com/users/{user_id}"
response = requests.get(url)
response.raise_for_status()
return response.json()
@pytest.mark.parametrize(
"user_id, expected_data",
[
(1, {"id": 1, "name": "John Doe"}),
(2, {"id": 2, "name": "Jane Smith"}),
],
)
@patch("requests.get")
def test_get_user_data(mock_get, user_id, expected_data):
mock_get.return_value.json.return_value = expected_data
mock_get.return_value.status_code = 200
data = get_user_data(user_id)
assert data == expected_data
mock_get.assert_called_once_with(f"https://api.example.com/users/{user_id}")
Selles näites on test_get_user_data funktsioon parameetritega erinevate user_id ja expected_data väärtustega. @patch dekoraator mockib requests.get funktsiooni. Pytest käivitab testfunktsiooni kaks korda, üks kord iga parameetrite komplekti jaoks, kusjuures mock on konfigureeritud tagastama vastava expected_data.
Parimad praktikad täiustatud fikstuuride kasutamisel
- Hoidke fikstuurid fookuses: Igal fikstuuril peaks olema selge ja konkreetne eesmärk.
- Kasutage sobivaid skoope: Valige sobiv fikstuuri skoop (funktsioon, moodul, seanss), et optimeerida ressursside kasutust.
- Dokumenteerige fikstuurid: Dokumenteerige selgelt iga fikstuuri eesmärk ja kasutusviis.
- Vältige liigset mockimist: Mockige ainult neid sõltuvusi, mis on vajalikud testitava koodi isoleerimiseks.
- Kirjutage selged kinnitused: Veenduge, et teie kinnitused (assertions) on selged ja spetsiifilised, kontrollides testitava koodi oodatavat käitumist.
- Kaaluge testipõhist arendust (TDD): Kirjutage oma testid enne koodi kirjutamist, kasutades fikstuure ja mocke arendusprotsessi suunamiseks.
Kokkuvõte
Pytesti täiustatud fikstuuritehnikad, sealhulgas parameetritega testimine ja mock-integratsioon, pakuvad võimsaid tööriistu robustsete, tõhusate ja hooldatavate testide kirjutamiseks. Nende tehnikate valdamisega saate oluliselt parandada oma Pythoni koodi kvaliteeti ja sujuvamaks muuta oma testimise töövoogu. Pidage meeles keskenduda selgete, fookustatud fikstuuride loomisele, sobivate skoopide kasutamisele ja põhjalike kinnituste kirjutamisele. Praktikaga suudate ära kasutada Pytesti fikstuurisüsteemi täieliku potentsiaali, et luua terviklik ja tõhus testimisstrateegia.