კოდის ტესტირება პითონში: Pytest ბიბლიოთეკა
ისწავლეთ პითონის კოდის ტესტირება Pytest-ის გამოყენებით. მარტივი გზამკვლევი დამწყებთათვის: ინსტალაცია, კლასების ტესტირება, Mocking და პარამეტრული ტესტები და სხვა
Pytest დამწყებთათვის: კოდის ტესტირება მარტივად
პროგრამირების სწავლისას, კოდის ტესტირება ის ეტაპია, რომელსაც ხშირად გამოვტოვებთ ხოლმე. არადა, შეცდომების ხელით ძებნას და უსასრულო print()-ების წერას ბევრად სჯობს ეს პროცესი ავტომატიზებული იყოს. დღეს Pytest-ს განვიხილავთ; ბიბლიოთეკას, რომელიც პითონში კოდის ტესტირებას ძალიან კომფორტულს ხდის. თანაც, კოდის ტესტირება ის ასპექტია, რაც არ არის სასურველი მთლიანად AI აგენტებს მივანდოთ!
ინსტალაცია და მომზადება
დავიწყოთ გარემოს მომზადებით. გახსენით ტერმინალი, დააინსტალირეთ ბიბლიოთეკა და დააჭირეთ ენთერს:
pip install pytestინსტალაციის დასრულების შემდეგ, შეგვიძლია გადავამოწმოთ ვერსია:
pytest --versionტერმინალში მიიღებთ მსგავს პასუხს:
pytest 8.0.0ყველაფერი მზადაა! მოდით, პრაქტიკისთვის ონლაინ მაღაზიის ფასდაკლების მარტივი სისტემა ავაწყოთ.
1. მთავარი ფუნქციის შექმნა (main.py)
პირველ რიგში, main.py ფაილში განვსაზღვროთ ფასდაკლების ლოგიკა:
def apply_discount(price: float) -> float:
if price >= 100:
return price * 0.8
elif price >= 50:
return price * 0.9
return price
print(apply_discount(120))ეკრანზე გამოვა:
96.0ლოგიკა მარტივია: apply_discount ამოწმებს პროდუქტის ფასს. 100 ლარზე ან მეტზე გვაქვს 20%-იანი ფასდაკლება, 50-დან 100 ლარამდე 10%-იანი, სხვა შემთხვევაში კი ფასი უცვლელი რჩება.
2. კოდის ტესტირება (test_main.py)
ახლა დავრწმუნდეთ, რომ სამივე პირობა გამართულად მუშაობს. ამისთვის ვქმნით test_main.py ფაილს:
from main import apply_discount
def test_apply_discount():
assert apply_discount(120) == 96.0
assert apply_discount(70) == 63.0
assert apply_discount(30) == 30.0
if __name__ == "__main__":
test_apply_discount()ტესტის გასაშვებად, უნდა გავხსნათ ტერმინალი და ავკრიფოთ შემდეგი ბრძანება:
pytest test_main.pyPytest-ის გავლით ამ ტესტის გაშვებისას შემდეგ სურათს დავინახავთ:
================ test session starts ================
collected 1 item
test_main.py . [100%]
================ 1 passed in 0.01s ==================პროცესი ასეთია: main.py-დან შემოგვაქვს ჩვენი ფუნქცია და assert-ის გამოყენებით ვამოწმებთ მაღალ, საშუალო და დაბალ ფასებს.
assert უბრალოდ ადარებს ფუნქციის დაბრუნებულ პასუხს ჩვენს მოლოდინთან. თუ მომავალში კოდს შევცვლით და რაიმე აირევა, Pytest მაშინვე გაგვაფრთხილებს ჩავარდნილი ტესტით, რაც სერიოზული შეცდომებისგან გვიცავს.
კლასების ტესტირება Pytest-ით
რეალურ პროექტებში ხშირად კლასებთან გვიწევს მუშაობა. საბედნიეროდ, Pytest-ს მათი ტესტირებაც ზუსტად ისევე მარტივად შეუძლია, როგორც ჩვეულებრივი ფუნქციების.
მაგალითისთვის ავიღოთ კინოთეატრის ბილეთების სისტემა.
1. კლასის შექმნა (main.py)
ჩვენს main.py ფაილში დავწეროთ CinemaTicket კლასი:
class CinemaTicket:
def check_age(self, age: int) -> str:
if age < 12:
return "Child ticket"
elif age < 60:
return "Adult ticket"
else:
return "Senior ticket"
def check_time(self, hour: float) -> str:
if hour < 12.0:
return "Morning discount"
elif hour < 18.0:
return "Regular price"
else:
return "Night show"
ticket = CinemaTicket()
print(ticket.check_age(25))
print(ticket.check_time(10.5))შედეგად მივიღებთ:
Adult ticket
Morning discountაქ ორი მეთოდი გვაქვს: check_age ასაკის მიხედვით ანაწილებს მომხმარებლებს (ბავშვი, ზრდასრული, პენსიონერი), ხოლო check_time სეანსის საათს ამოწმებს (დილის, ჩვეულებრივი, თუ ღამის).
2. კლასის ტესტირება (test_main.py)
ახლა ვნახოთ როგორ იტესტება ეს კლასი. ორგანიზებულობისთვის, თითოეულ მეთოდს თავისი ტესტი დავუწეროთ:
from main import CinemaTicket
def test_check_age():
ticket = CinemaTicket()
assert ticket.check_age(10) == "Child ticket"
assert ticket.check_age(35) == "Adult ticket"
assert ticket.check_age(65) == "Senior ticket"
def test_check_time():
ticket = CinemaTicket()
assert ticket.check_time(9.5) == "Morning discount"
assert ticket.check_time(15.0) == "Regular price"
assert ticket.check_time(21.0) == "Night show"ტერმინალში pytest test_main.py-ის გაშვებისას ვნახავთ:
================ test session starts ================
collected 2 items
test_main.py .. [100%]
================ 2 passed in 0.02s ==================კლასის ტესტირების მთავარი პრინციპი ისაა, რომ assert-მდე ამ კლასის ობიექტი უნდა შევქმნათ (ticket = CinemaTicket()). ორივე ტესტმა სამ-სამი ვარიანტი წარმატებით გაიარა.
შეცდომების (Exceptions) ტესტირება
პროგრამა მხოლოდ სწორ მონაცემებზე არ უნდა მუშაობდეს. როცა რაღაც სწორად არ მოხდება, კოდმა სათანადო შეცდომა (Exception) უნდა დააბრუნოს. მოდით, ტესტირების ეს ნაწილიც ვნახოთ.
ამისთვის გამოვიყენოთ ციფრული საფულის კლასი.
1. შეცდომის გენერირება (main.py)
class Wallet:
def pay(self, price: float, balance: float) -> float:
if price > balance:
raise ValueError("Not enough money!")
return balance - price
my_wallet = Wallet()
print(my_wallet.pay(20, 50))ეკრანზე გამოიტანს:
30pay მეთოდი მარტივ შემოწმებას აკეთებს: თუ ფასი ბალანსს აღემატება, სპეციალურად ვისვრით ValueError-ს. სხვა შემთხვევაში ვაბრუნებთ ნაშთს.
2. შეცდომის შემოწმება ტესტში (test_main.py)
ახლა შევამოწმოთ როგორც ჩვეულებრივი გადახდა, ისე უფულობის სცენარი. აქ უკვე თავად pytest ბიბლიოთეკის დაიმპორტება დაგვჭირდება.
from main import Wallet
import pytest
def test_pay_success():
w = Wallet()
assert w.pay(20, 50) == 30
assert w.pay(50, 50) == 0
def test_pay_error():
w = Wallet()
with pytest.raises(ValueError, match="Not enough money!"):
w.pay(100, 50)გაშვების შედეგი:
================ test session starts ================
collected 2 items
test_main.py .. [100%]
================ 2 passed in 0.02s ==================პირველ ფუნქციაში ჩვეულებრივი წარმატებული გადახდები გავტესტეთ.
მთავარი ინტერესი მეორე ფუნქციაშია: with pytest.raises(ValueError, match="Not enough money!") ეუბნება Pytest-ს, რომ შიგნით გაშვებული კოდისგან შეცდომას მოელის. თუ ფუნქცია ამ შეცდომას არ დააბრუნებს ზუსტად ამ ტექსტით, ტესტი ჩავარდება.
პარამეტრული ტესტირება: ბევრი მონაცემი ერთი ფუნქციით
წარმოიდგინეთ, რომ 10 სხვადასხვა ვარიანტი გვაქვს შესამოწმებელი. თითოეულისთვის assert-ის წერა ძალიან მოსაწყენია. აქ გვშველის საოცარი ინსტრუმენტი: @pytest.mark.parametrize.
ვნახოთ ეს სტუდენტის ნიშნების გამომთვლელ პროგრამაზე.
1. მთავარი ფუნქცია (main.py)
def get_grade(score: int) -> str:
if score < 50:
return "F"
elif score < 70:
return "C"
elif score < 90:
return "B"
else:
return "A"
print(get_grade(85))გაშვების შემდეგ ვხედავთ:
Bვიღებთ ქულას და პირობების მიხედვით ვაბრუნებთ შესაბამის შეფასებას.
2. პარამეტრული ტესტი (test_main.py)
ოთხი ცალკეული ხაზის ნაცვლად, შეგვიძლია ეს ყველაფერი ელეგანტურად ერთ სიაში მოვაქციოთ:
from main import get_grade
import pytest
@pytest.mark.parametrize("score,expected", [
(30, "F"),
(60, "C"),
(85, "B"),
(95, "A")
])
def test_get_grade(score, expected):
assert get_grade(score) == expectedტესტის შედეგი:
================ test session starts ================
collected 4 items
test_main.py .... [100%]
================ 4 passed in 0.03s ==================რა ხდება ამ დროს კულისებში?
@pytest.mark.parametrize ტესტს ამუშავებს იმდენჯერ, რამდენი მონაცემიც გვაქვს სიაში. პირველად score იღებს 30-ს, expected "F"-ს, და მოწმდება. მერე იგივე მეორდება 60-ზე და ასე შემდეგ.
თუ ახალი პირობის (მაგალითად, "A+") დამატება დაგვჭირდება, ფუნქციას არ ვეხებით, უბრალოდ ერთ ხაზს ვამატებთ სიაში.
გარე სერვისების ტესტირება (Mocking): როგორ შევამოწმოთ API კავშირები
პროგრამა თითქმის ყოველთვის უკავშირდება გარე API-ებს. ტესტირებისას რეალური ინტერნეტ კავშირის გამოყენება არ არის სასურველი: ანელებს პროცესს, ხარჯავს ლიმიტებს და დამოკიდებულია ქსელის სტაბილურობაზე.
აქ შემოდის Mocking-ი, სერვერის პასუხის იმიტაცია. დავიწყოთ საჭირო ბიბლიოთეკის დაყენებით:
pip install pytest-mockავიღოთ მაგალითი: ფუნქცია ამოწმებს, აქვს თუ არა მომხმარებელს Premium პაკეტი:
main.py
import requests
def verify_subscription(url: str) -> dict:
response = requests.get(url)
data = response.json()
return {
"user": data["user"],
"is_premium": data["plan"] == "premium"
}ჩვენი მიზანია შევამოწმოთ, სწორად ანიჭებს თუ არა ფუნქცია True-ს is_premium ველს, როცა API "plan": "premium"-ს აბრუნებს.
test_main.py
რეალური მოთხოვნის ასარიდებლად pytest-mock-ის mocker-ს ვიყენებთ:
from main import verify_subscription
def test_premium_user(mocker):
mock_get = mocker.patch("main.requests.get")
mock_get.return_value.json.return_value = {
"user": "nika_dev",
"plan": "premium"
}
result = verify_subscription("https://api.example.com/sub/1")
assert result == {
"user": "nika_dev",
"is_premium": True
}კოდის განხილვა:
- პირველ რიგში, შემოგვაქვს მთავარი ფუნქცია ტესტირებისთვის.
- ტესტის ფუნქციას გადავცემთ
mockerობიექტს, რომელიც გარე კავშირების გაყალბებაში დაგვეხმარება. mocker.patch-ით დროებით ვაჩერებთrequests.get-ს, რათა ინტერნეტში არ გავიდეთ.- ამის შემდეგ, ვაკონფიგურირებთ და წინასწარ ვეუბნებით, რა ყალბი JSON პასუხი უნდა დაგვიბრუნოს ამ მოთხოვნამ.
- შემდეგ ჩვეულებრივად ვიძახებთ ფუნქციას.
- და ბოლოს,
assert-ით ვრწმუნდებით, რომ ფუნქციამ ყალბი მონაცემი სწორად დაამუშავა და სტატუსი მართებულად მიანიჭა.
რატომ არ აქვს მნიშვნელობა URL-ს?
რადგან mocker.patch ინტერნეტ კავშირს ბლოკავს, რეალური requests.get სრულად იგნორირებას უკეთებს გადაცემულ მისამართს და პირდაპირ ჩვენს გამზადებულ პასუხს აბრუნებს. თუმცა, პითონმა შეცდომა რომ არ ამოაგდოს, ფუნქციას არგუმენტი მაინც უნდა გადავცეთ, თუნდაც ეს სრულიად უაზრო ტექსტი იყოს.
საპირისპირო შემთხვევის შემოწმება
იმის საჩვენებლად, რომ URL მართლა იგნორირებულია, მეორე ტესტში მისამართის ნაცვლად უბრალოდ "ბანანი" გადავცეთ და ისე შევამოწმოთ უფასო მომხმარებლის სტატუსი:
def test_free_user(mocker):
mock_get = mocker.patch("main.requests.get")
mock_get.return_value.json.return_value = {
"user": "nika_dev",
"plan": "free"
}
result = verify_subscription("ბანანი")
assert result == {
"user": "nika_dev",
"is_premium": False
}ეს ტესტები სრულად იზოლირებულია. ჩვენ მხოლოდ შიდა ლოგიკას ვამოწმებთ კონტროლირებად გარემოში.
გაშვებისას მივიღებთ:
================ test session starts ================
collected 2 items
test_main.py .. [100%]
================ 2 passed =================პროცესი იდენტურია:
- აქაც ვიყენებთ
mocker-ს ტესტის ფუნქციაში. - ვაჩერებთ
requests.get-ს. - ვამზადებთ ახალ ყალბ პასუხს, ამჯერად უფასო მომხმარებლის გეგმით.
- ვიძახებთ ფუნქციას სიტყვით "ბანანი", რითაც ვამტკიცებთ, რომ URL-ს მნიშვნელობა არ აქვს.
- და ვამოწმებთ, სწორად მიენიჭა თუ არა მომხმარებელს
Falseსტატუსი.
გსურთ დაწეროთ თქვენი პირველი პროფესიონალური კოდი? 🚀
ტესტირება და ხარისხის კონტროლი ის უმთავრესი უნარებია, რომლებიც დამწყებ ენთუზიასტს ნამდვილი, მაღალანაზღაურებადი პროფესიონალისგან განასხვავებს. თუ გსურთ გაცდეთ მხოლოდ საბაზისო სინტაქსს, ისწავლოთ სუფთა არქიტექტურა, Unit ტესტების წერა და რეალური, მასშტაბური სისტემების აგება, ScaleUp Academy კურსები დაგეხმარებათ:
- 👉 Python & AI ინჟინერიის კურსი: შეისწავლეთ პითონი ნულიდან პროფესიონალურ დონემდე. დაეუფლეთ Back-End დეველოპმენტს, API-ების შექმნას, Pytest-ით ტესტირებას და AI მოდელების ინტეგრაციას გამოცდილი მენტორის ხელმძღვანელობით.
- 👉 SQL & მონაცემთა ბაზები: ისწავლეთ, როგორ დააპროექტოთ გამართული მონაცემთა ბაზები (PostgreSQL) და უზრუნველყოთ მონაცემების მთლიანობა და უსაფრთხოება.
- 👉 Java Back-End დეველოპმენტი: მათთვის, ვისაც სურს კორპორატიული დონის Back-end სისტემების შექმნა.
ნუ გადადებთ თქვენს პროფესიულ ზრდას! ნახეთ პროგრამირების კურსები ScaleUp.ge-ზე, გაეცანით სასწავლო პროგრამებს და გადადგით პირველი ნაბიჯი წარმატებული ტექ-კარიერისკენ დღესვე.
ავტორის შესახებ
გიგი ჩაკაბერია
Back-end დეველოპერი და მენტორი