Kurs
Python projeleriniz karmaşıklık ve boyut olarak büyüdükçe, sağlam testlerin önemi de artar. Testler, kodunuzun beklendiği gibi çalıştığından emin olmanıza ve uygulamanın tüm bölümlerinde kaliteyi korumanıza yardımcı olur.
Uygulamanız farklı veritabanı türleri veya OpenAI GPT ya da Anthropic Claude gibi harici API’lerle etkileşime girdiğinde, bu bağımlılıkları test etmek zorlayıcı olabilir. İşte burada mocking devreye girer.
Mocking, nesneleri, fonksiyonları ve davranışları taklit etmenize olanak tanıyarak testlerinizi daha izole ve öngörülebilir hale getirir. Ayrıca, karmaşıklık veya beklenmedik davranışlar getirebilecek bağımlılıklardan izole ederek test etmek istediğiniz belirli fonksiyona odaklanmanızı sağlar.
Bu blogda, pytest için güçlü bir eklenti olan pytest-mock’u Python testlerinizde mock’ları verimli şekilde uygulamak için nasıl kullanacağınızı öğreneceksiniz. Bu eğitimin sonunda, testlerinizi daha güçlü kılacak mocking tekniklerini eklemeye hazır olacaksınız.
pytest-mock nedir?
pytest-mock, popüler Python test çerçevesi pytest için mocking yeteneklerine kolay erişim sağlayan bir eklentidir. Python’un yerleşik unittest’i üzerine inşa edilmiştir ve test sırasında mocking sürecini basitleştirir.

pytest çerçevesi logosu
pytest-mock, okunabilirliği artırır ve pytest’e daha yerel bir yaklaşımla testlerde mock’ları uygulamayı kolaylaştırır. İster tekil fonksiyonları, ister sınıf metotlarını ya da tüm nesneleri mock’lamak isteyin, pytest-mock ekstra karmaşıklık katmadan etkili test için gereken esnekliği sunar.
Okunabilirliği artıran temel nedenlerden biri, pytest-mock’un deklaratif test stilidir; bu sayede beklenen çağrıları ve davranışları testlerinizde doğrudan belirtebilirsiniz. Bu, kodu yazmayı, okumayı ve sürdürmeyi kolaylaştırırken, dışsal davranışı değiştirmeyen uygulama değişikliklerine karşı dayanıklı kalmasını sağlar.
pytest-mock Kurulumu
Projeniz için sanal bir ortam oluşturmak, bağımlılıkların izole edilmesini sağlamak adına en iyi uygulamalardan biridir. Henüz yeni bir ortam oluşturmadıysanız, aşağıdaki komutlarla bir conda ortamı yaratabilirsiniz:
# create a conda environment
conda create --name yourenvname python=3.11
# activate conda environment
conda activate yourenvname
pytest ve pytest-mock yükleme
pytest ve pytest-mock’u ortamınıza doğrudan pip veya conda ile kurun:
pip install pytest
pip install pytest-mock
Kurulumu doğrulama
pytest ve pytest-mock’u yükledikten sonra, aşağıdaki komutu çalıştırarak kurulumun başarılı olduğunu doğrulayın:
pytest --version
pytest-mock ile Mocking’in Temelleri
Mocking’i, gerçek davranışı taklit eden “sahte” bir bileşen oluşturmak olarak düşünebilirsiniz.
Özellikle fonksiyonların veya sınıfların, veritabanları ya da harici API’ler gibi dış bileşenlerle nasıl etkileştiğini test etmeniz gerektiğinde faydalıdır.
Mocking’e giriş
Bir mock nesnesi, gerçek bir nesnenin davranışını taklit eder. Genellikle birim testlerinde bileşenleri izole etmek ve bağımlı kodu çalıştırmadan davranışlarını test etmek için kullanılır.
Örneğin, veritabanına gerçekten bağlanmadan bir veritabanı çağrısını simüle etmek için bir mock nesnesi kullanabilirsiniz.
Mocking, gerçek nesnenin şu durumlarında özellikle kullanışlıdır:
- Oluşturulması veya yapılandırılması zorsa.
- Kullanımı zaman alıyorsa (ör. uzak bir veritabanına erişmek).
- Test sırasında kaçınılması gereken yan etkileri varsa (ör. e-posta göndermek, maliyet oluşturmak).
mocker fixture’ını kullanma
pytest-mock, mock nesneleri oluşturmayı ve kontrol etmeyi kolaylaştıran bir mocker fixture’ı sağlar. mocker fixture’ı güçlüdür ve pytest testlerinize entegre olur.
💡Fixture’lar nedir?Python’da fixture’lar, testler için gerekli kaynakları (örneğin veritabanları veya dosyalar) kurup kaldıran, yeniden kullanılabilir bileşenlerdir. Tutarlılığı sağlar, tekrarları azaltır ve test kurulumunu basitleştirir. |
Bir örnek görelim:
def fetch_weather_data(api_client):
response = api_client.get("https://api.weather.com/data")
if response.status_code == 200:
return response.json()
else:
return None
fetch_weather_data fonksiyonu, verileri almak için harici bir hava durumu API’sine bağlıdır; ancak testler sırasında API’yi çağırmak gereksiz maliyetlere yol açabilir.
Bunu önlemek için, testlerinizde API’nin davranışını simüle etmek üzere mocking kullanabilirsiniz; böylece uygulama harici çağrılar yapmadan test edilir. Mocking bu şekilde yardımcı olabilir:
def test_fetch_weather_data(mocker):
# Create a mock API client
mock_api_client = mocker.Mock()
# Mock the response of the API client
mock_response = mocker.Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"temperature": "20C", "condition": "Sunny"}
# Set the mock API client to return the mocked response
mock_api_client.get.return_value = mock_response
# Call the function with the mock API client
result = fetch_weather_data(mock_api_client)
# Assert that the correct data is returned
assert result == {"temperature": "20C", "condition": "Sunny"}
mock_api_client.get.assert_called_once_with("https://api.weather.com/data")
test_fetch_weather_data() içinde, mocker fixture’ını kullanarak bir sahte API istemcisi mock_api_client ve bir mock_response oluşturuyoruz. Mock yanıt, 200 durum kodu ve hava bilgisi içeren JSON verisi döndürecek şekilde gerçek bir API çağrısını taklit edecek biçimde yapılandırılır.
Mock API istemcisinin get metodu bu mock yanıtı döndürecek şekilde ayarlanır. fetch_weather_data() test edildiğinde, gerçek bir API çağrısı yapmak yerine mock API istemcisiyle etkileşir.
Bu yaklaşım, fonksiyonun beklendiği gibi davranmasını sağlarken testleri hızlı, maliyet-etkin ve dış sistemlerden bağımsız tutar.
Sondaki assert ifadesi, döndürülen verinin beklenen çıktı ile eşleştiğini doğrular.
Son satır olan mock_api_client.get.assert_called_once_with("https://api.weather.com/data"), özellikle mock API istemcisinin get metodunun tam olarak bir kez ve doğru argümanla çağrıldığını kontrol eder. Bu, fonksiyonun API ile beklenen şekilde etkileşime girdiğini doğrulamaya yardımcı olarak teste ekstra bir doğrulama katmanı ekler.
pytest-mock için Yaygın Kullanım Senaryoları
Mocking, belirli fonksiyonları veya bileşenleri izole etmeniz gereken birim testleri dahil olmak üzere çeşitli senaryolarda kullanışlıdır. Bazı yaygın kullanım örneklerine bakalım:
1. Fonksiyonları veya sınıf metotlarını mock’lamak
Başka fonksiyonlara veya metotlara bağlı bir fonksiyonu test etmeniz gerekebilir; ancak test sırasında bunların davranışını kontrol etmek isteyebilirsiniz. Mocking, bu bağımlılıkları önceden tanımlanmış davranışlarla değiştirme olanağı sunar:
# production code
def calculate_discount(price, discount_provider):
discount = discount_provider.get_discount()
return price - (price * discount / 100)
# test code
def test_calculate_discount(mocker):
# Mock the get_discount method
mock_discount_provider = mocker.Mock()
mock_discount_provider.get_discount.return_value = 10 # Mocked discount value
# Call the function with the mocked dependency
result = calculate_discount(100, mock_discount_provider)
# Assert the calculated discount is correct
assert result == 90
mock_discount_provider.get_discount.assert_called_once()
Bu testte, discount_provider’ın get_discount() metodu, önceden tanımlanmış bir değer (%%10) döndürecek şekilde mock’lanmıştır. Bu, calculate_discount() fonksiyonunu discount_provider’ın gerçek uygulamasından izole eder.
2. Zamana bağlı kodu mock’lamak
Zaman veya gecikme içeren fonksiyonları test ederken, zamana ilişkin metotları mock’lamak beklemeyi veya gerçek zamanı manipüle etmeyi önler:
# production code
import time
def long_running_task():
time.sleep(5) # Simulate a long delay
return "Task Complete"
# test code
def test_long_running_task(mocker):
# Mock the sleep function in the time module
mocker.patch("time.sleep", return_value=None)
# Call the function
result = long_running_task()
# Assert the result is correct
assert result == "Task Complete"
Burada, time.sleep test sırasında gerçek gecikmeyi atlamak için patch’lenmiştir. Böylece test, 5 saniye beklemeden hızlıca çalışır.
3. Nesne özniteliklerini mock’lamak
Bazen, test sırasında farklı durumları simüle etmek için bir nesnenin özniteliklerinin veya özelliklerinin davranışını mock’lamak isteyebilirsiniz:
# production code
class User:
def __init__(self, name, age):
self.name = name
self.age = age
@property
def is_adult(self):
return self.age >= 18
# test code
def test_user_is_adult(mocker):
# Create a User object
user = User(name="Alice", age=17)
# Mock the is_adult property
mocker.patch.object(User, "is_adult", new_callable=mocker.PropertyMock, return_value=True)
# Assert the mocked property value
assert user.is_adult is True
Bu örnekte, User sınıfının is_adult() özelliği, True döndürecek şekilde mock’lanmıştır; gerçek yaştan bağımsızdır. Bu, nesne durumlarına bağlı senaryoları test etmek için kullanışlıdır.
pytest-mock ile İleri Mocking Teknikleri
Temel mocking ile rahat ettikten sonra, pytest-mock daha karmaşık senaryoları ele almak için gelişmiş yetenekler sunar.
1. Yan etkileri mock’lamak
Mock’lar sadece dönüş değerlerini değil, aynı zamanda istisna fırlatma veya dönüş değerlerini dinamik olarak değiştirme gibi davranışları da taklit edebilir. Bu, mock’lara “yan etkiler” eklenerek gerçekleştirilir:
import pytest
# production code
def process_payment(payment_gateway, amount):
response = payment_gateway.charge(amount)
if response == "Success":
return "Payment processed successfully"
else:
raise ValueError("Payment failed")
# test code
def test_process_payment_with_side_effects(mocker):
# Mock the charge method of the payment gateway
mock_payment_gateway = mocker.Mock()
# Add side effects: Success on first call, raise exception on second call
mock_payment_gateway.charge.side_effect = ["Success", ValueError("Insufficient funds")]
# Test successful payment
assert process_payment(mock_payment_gateway, 100) == "Payment processed successfully"
# Test payment failure
with pytest.raises(ValueError, match="Insufficient funds"):
process_payment(mock_payment_gateway, 200)
# Verify the mock's behavior
assert mock_payment_gateway.charge.call_count == 2
side_effect özelliği, mock’un sonraki çağrılarda farklı değerler döndürmesini veya istisna fırlatmasını sağlar. Bu örnekte, ilk charge çağrısı "Success" döndürürken, ikinci çağrı bir ValueError fırlatır. Bu, tek bir test içinde çeşitli senaryoları sınamak için kullanışlıdır.
2. Fonksiyonları “spy” ile izleme
Spy kullanımı, gerçek bir fonksiyonun nasıl çağrıldığını (çağrı sayısı ve iletilen argümanlar dahil) takip etmenizi sağlar. Orijinal mantığı çalıştırmaya devam ederken, fonksiyonun beklendiği gibi çağrıldığından emin olmak istediğiniz durumlarda özellikle faydalıdır:
# production code
def log_message(logger, message):
logger.info(message)
return f"Logged: {message}"
# test code
def test_log_message_with_spy(mocker):
# Spy on the info method of the logger
mock_logger = mocker.Mock()
spy_info = mocker.spy(mock_logger, "info")
# Call the function
result = log_message(mock_logger, "Test message")
# Assert the returned value
assert result == "Logged: Test message"
# Verify the spy behavior
spy_info.assert_called_once_with("Test message")
assert spy_info.call_count == 1
spy() metodu, bir gerçek metodun davranışını değiştirmeden nasıl çağrıldığını takip eder. Burada, logger’ın info metodunu doğru mesajla çağrıldığından emin olmak için izliyor, aynı zamanda metodun normal şekilde çalışmasına izin veriyoruz.
pytest-mock Kullanımı için En İyi Uygulamalar
Mocking güçlüdür; ancak dikkatsiz kullanım testlerinizde sorunlara yol açabilir. Aşağıdaki en iyi uygulamaları izlemenizi öneririm:
1. Aşırı mocking’den kaçının
Çok fazla mock kullanmak, dışsal davranış değişmese bile içsel kod değişikliklerinde bozulan kırılgan testlere yol açabilir. Yalnızca gerekli olanı mock’lamaya çalışın ve mümkün olduğunda gerçek uygulamalara güvenin.
Aşırı mocking, testlerinizi kodunuzun uygulama ayrıntılarına sıkı sıkıya bağlayabilir; bu da küçük yeniden düzenlemelerin bile gereksiz yere testlerin başarısız olmasına neden olabileceği anlamına gelir. Bunun yerine, yalnızca sistemin dışsal olan veya belirgin yan etkileri bulunan kısımlarını mock’lamaya odaklanın.
2. Açık isimlendirme kuralları kullanın
Nesneleri mock’larken açıklayıcı isimler kullanın. Bu, testlerinizi daha okunabilir ve sürdürülebilir kılar. Örneğin, mock_function() gibi genel isimler yerine mock_api_response() gibi daha açıklayıcı isimler kullanın.
Açık isimlendirme kuralları, her mock’un amacını başkalarının anlamasını kolaylaştırır; kafa karışıklığını azaltır ve zamanla test setinin bakımını kolaylaştırır.
3. Testleri basit ve odaklı tutun
Her test tek bir davranışa veya senaryoya odaklanmalıdır. Bu, hata ayıklamayı basitleştirir ve testlerinizi anlaşılır kılar. İyi odaklanmış bir test, bir şeyler ters gittiğinde daha net geri bildirim sağlama eğilimindedir ve sorunları tespit edip düzeltmeyi kolaylaştırır.
Sonuç
Bu blogda, pytest-mock ile mocking’in temellerini inceledik ve Python testlerimizi iyileştirmek için nasıl kullanacağımızı öğrendik. Fonksiyon ve metotların temel mocking’inden, yan etkiler ekleme ve fonksiyonları izleme gibi daha gelişmiş tekniklere kadar her şeyi ele aldık.
Mocking, özellikle karmaşık sistemler veya harici bağımlılıklarla çalışırken, güvenilir ve sürdürülebilir testler oluşturmanın vazgeçilmez bir aracıdır. Projelerinize pytest-mock’u dahil ederek daha izole, öngörülebilir ve bakımı kolay testler yazabilirsiniz.
Test becerilerinizi geliştirmeye devam etmek için, Datacamp’teki Introduction to Testing in Python adlı ücretsiz kursa göz atmanızı öneririm.
SSS
Mocking, gerçek bileşenleri test etmekten nasıl farklıdır?
Mocking, gerçek bileşenleri taklit edilenlerle değiştirerek dış sistemlere veya gerçek verilere bağlı kalmadan izole ve maliyet-etkin test yapmayı sağlar.
pytest-mock’u diğer test çerçeveleriyle kullanabilir miyim?
pytest-mock, özellikle pytest için tasarlanmıştır ve onunla sorunsuz entegre olur; ancak bağımsız olarak kullanılabilen Python’un unittest.mock kütüphanesi üzerine inşa edilmiştir.
pytest-mock’ta fixture’lar zorunlu mudur?
mocker gibi fixture’lar mocking verimliliğini artırsa da, fixture kullanmadan da pytest içinde doğrudan unittest.mock kullanabilirsiniz; ancak entegrasyon daha sınırlı olur.

