Python Test İşlemleri

Bu yazıda, Python test konusuna odaklanacağız. Python’un test yazılımına destek sağlayan özellikler arasında çeşitli test çerçeveleri (unittest, pytest, doctest vb.), hata ayıklama yetenekleri, geniş kütüphane desteği ve mock nesneler oluşturmayı kolaylaştıran özellikler bulunmaktadır.

Temel Test Kavramları ve Terminolojisi

Test süreci, bir yazılımın belirli bir girdi seti ile beklenen sonuçları verip vermediğini kontrol etme işlemidir. Test sürecinin temel amacı, yazılımdaki hataları, eksiklikleri veya gereksinimlere uygun olmayan özellikleri belirlemek, düzeltmek, yazılımın güvenilirliğini ve performansını doğrulamak ve son kullanıcıya daha iyi bir deneyim sunmaktır.

Test Terminolojisi

  1. Test Case: Bir test senaryosunu veya testin bir parçasını tanımlayan belge veya yapı.
  2. Test Suite: Bir grup test case’inin veya testlerin bir araya getirilmesiyle oluşturulan ve birlikte çalıştırılan testlerin toplamı.
  3. Test Fixture: Bir testin başarılı bir şekilde çalışabilmesi için gerekli olan hazırlık işlemleri ve koşullar.
  4. Test Runner: Test case’leri çalıştıran ve sonuçları raporlayan yazılım veya araç.
  5. Test Coverage: Bir test setinin bir yazılımın ne kadarını test ettiğini belirten ölçüm.

Python’da Test Yazmak İçin Kullanılan Popüler Kütüphaneler

  1. unittest: Python’ın standart kütüphanesinin bir parçası olan unittest, xUnit mimarisine dayalı bir test çerçevesidir. Testleri organize etmek ve çalıştırmak için bir dizi araç ve asseration sunar.
  2. pytest: pytest, Python programlarını test etmek için kullanılan güçlü ve esnek bir çerçevedir. Basit syntaxı ve geniş özellik seti ile bilinir. Ayrıca, fixture’lar ve parametrize testler gibi özellikler sunar.
  3. doctest: doctest, Python modüllerinde, işlevlerde ve sınıflarda yerleşik belgelere yazılan testleri çalıştırır. Bu, testlerin ve belgelerin aynı yerde tutulmasını sağlar.
  4. tox: Farklı Python sürümlerinde ve ortamlarda birim testlerini çalıştırmak için kullanılan bir araçtır.
  5. coverage: Birim testleri tarafından hangi kodların kapsanmadığını gösteren bir kütüphanedir.
  6. nose: nose, unittest’in genişletilmiş bir versiyonudur ve daha karmaşık test senaryolarını destekler. Ancak, aktif olarak bakımı yapılmamaktadır ve yerini “nose2” almıştır.
  7. behave: behave, Davranış-Tabanlı Geliştirme (BDD) için bir çerçeve sunar. Kullanıcı hikayelerine dayalı senaryoları test etmeyi kolaylaştırır.
  8. robotframework: Kabul testleri yazmak için kullanılır.
  9. Selenium: Selenium, özellikle web uygulamaları için otomatik testler yazmak üzere tasarlanmış bir kütüphanedir. Web tarayıcıları üzerinde otomatik işlemler yaparak uygulamanın davranışını kontrol eder.
  10. pylint: Kodunuzu analiz etmek ve olası hataları ve kod kokularını bulmak için kullanılan bir araçtır.
  11. mypy: Kodunuzu statik olarak analiz etmek ve tip hatalarını bulmak için kullanılan bir araçtır.
Pyhton Testing Roadmap

Python Test Türleri

  1. Birim Testi (Unit Test)Unit test, yazılımın belirli bir özelliğinin veya fonksiyonunun doğru çalışıp çalışmadığını kontrol etmek için kullanılır ve hataları erken aşamada tespit etmek için önemlidir. Python’un unittest modülü, unit testlerin kolayca yazılmasını ve çalıştırılmasını sağlar. Test case’ler, unittest.TestCase sınıfından türetilen bir sınıf içinde tanımlanan bir metoddur ve assert metodlarıyla beklenen ve gerçek sonuçlar karşılaştırılır.
    import unittest
    
    # Test edilecek fonksiyon
    def square(x):
        return x ** 2
    
    # Test case
    class TestSquare(unittest.TestCase):
        def test_positive_number(self):
            self.assertEqual(square(3), 9)
    
        def test_negative_number(self):
            self.assertEqual(square(-2), 4)
    
    # Testlerin çalıştırılması
    if __name__ == '__main__':
        unittest.main()
    
  2. Entegrasyon Testi (Integration Test)Entegrasyon testleri, yazılımın farklı bileşenleri arasındaki etkileşimi kontrol eder ve hataları erken aşamada tespit etmeye yardımcı olur. Python’da, unittest modülü kullanılarak entegrasyon testleri yazılır ve genellikle ayrı bir dosyada saklanır. Bu testler, test sürecinin bir parçası olarak çalıştırılır.
    import unittest
    from myproject import MyModule
    
    class TestIntegration(unittest.TestCase):
        def setUp(self):
            self.module = MyModule()
    
        def test_feature(self):
            self.module.setup_feature()
            result = self.module.run_feature()
            self.assertEqual(result, expected_result)
    
    if __name__ == '__main__':
        unittest.main()
    

    Bu örnekte, setUp() metodu test başlamadan önce çalışır ve test edilecek modülü başlatır. test_feature() metodu ise özellikleri çalıştırır ve sonucu beklenen sonuçla karşılaştırır.

    Python’da entegrasyon testlerinde dış bağımlılıkları kontrol etmek için unittest.mock modülü kullanılır. Mock objeler, gerçek objelerin yerine geçerek belli koşullar altında testlerin nasıl işleyeceğini kontrol etme imkanı sağlar. Gerçek işlevsellik sunmazlar, fakat gerçek objelerin arayüzünü taklit ederler ve belirli bir durumu simüle etmek için önceden belirlenmiş davranışları olabilir. Örneğin, bir API çağrısını mock etmek isteyebiliriz. Bu, gerçek API çağrısının zaman alıcı veya maliyetli olduğu durumlarda oldukça yararlı olabilir.

    import unittest
    from unittest.mock import patch
    import requests
    
    # API'ye get request yapacak olan fonksiyon
    def get_data_from_api(url):
        response = requests.get(url)
        if response.status_code == 200:
            return response.json()
        else:
            return None
    
    # Test case
    class TestApiCall(unittest.TestCase):
        @patch('requests.get')
        def test_get_data_from_api(self, mock_get):
            # Mock nesnesinin döndüreceği yanıtı belirleyelim
            mock_response = mock_get.return_value
            mock_response.status_code = 200
            mock_response.json.return_value = {'key': 'value'}
    
            url = '<http://fakeurl.com>'
            result = get_data_from_api(url)
    
            # API çağrısının doğru url ile yapıldığını kontrol edelim
            mock_get.assert_called_once_with(url)
            # Fonksiyonun doğru değeri döndürdüğünü kontrol edelim
            self.assertEqual(result, {'key': 'value'})
    
    # Testlerin çalıştırılması
    if __name__ == '__main__':
        unittest.main()
    

    Bu örnekte, requests.get fonksiyonunu mock ediyoruz ve get_data_from_api fonksiyonunun doğru URL ile çağrıldığını ve beklediğimiz sonucu döndürdüğünü kontrol ediyoruz.

  3. Kabul Testi (Acceptance Test)Acceptance test, yazılımın belirli işlevlerini kullanıcı perspektifinden kontrol eder ve genellikle geliştirme sürecinin son aşamalarında yapılır. Python, behave adlı bir kütüphane ile bu testlerin otomatikleştirilmesini sağlar, bu da BDD yaklaşımını benimser ve kullanıcı hikayelerine dayalı senaryoları test etmeyi kolaylaştırır.
    from behave import given, when, then
    from some_module import System
    
    @given('the system has booted')
    def step_given(context):
        context.system = System()
    
    @when('a user logs in')
    def step_when(context):
        context.system.login('user', 'passwd')
    
    @then('the user should see a welcome message')
    def step_then(context):
        assert context.system.has_welcome_message()
    

    Yukarıdaki örneklerde, given, when, then dekoratörleri kullanılarak BDD senaryoları oluşturulmuştur. given ile başlangıç durumu, when ile senaryonun gerçekleştiği durum ve then ile sonuç durumu tanımlanır. Bu yaklaşım, testlerin anlaşılırlığını ve okunabilirliğini artırır.

  4. Fonksiyonel Testler (Functional Tests)Fonksiyonel testler, yazılımın işlevlerinin doğru çalışıp çalışmadığını kontrol eder ve genellikle kullanıcı senaryoları ve iş akışları üzerinden gerçekleştirilir. Python ve Selenium, bu tür testlerin yazılması ve otomatize edilmesi için kullanılır, örneğin Selenium WebDriver ile bir web sitesine giriş yapma ve başarılı giriş sonrası mesajı kontrol etme.
    from selenium import webdriver
    
    def test_login():
        driver = webdriver.Firefox()
        driver.get("<http://www.example.com/login>")
    
        username_field = driver.find_element_by_name("username")
        password_field = driver.find_element_by_name("password")
        submit_button = driver.find_element_by_name("submit")
    
        username_field.send_keys("user")
        password_field.send_keys("pass")
        submit_button.click()
    
        assert "Welcome, user!" in driver.page_source
    
        driver.quit()
    
  5. Regresyon Testleri (Regression Tests)Regression testleri, yazılımın mevcut özelliklerinin yeni eklemeler veya değişiklikler sonucu bozulup bozulmadığını kontrol eder. Python’un unittest modülü ile regression testleri yazılabilir. Otomatik regression test süitleri oluşturmak için unittest.TestSuite sınıfı kullanılır.
    import unittest
    
    class TestMyFunction(unittest.TestCase):
        def test_addition(self):
            self.assertEqual(add(2, 2), 4)
    
        def test_subtraction(self):
            self.assertEqual(subtract(2, 2), 0)
    
    def suite():
        suite = unittest.TestSuite()
        suite.addTest(TestMyFunction('test_addition'))
        suite.addTest(TestMyFunction('test_subtraction'))
        return suite
    
    if __name__ == '__main__':
        runner = unittest.TextTestRunner()
        runner.run(suite())
    

    Bu örnekte, add ve subtract fonksiyonlarını test eden iki test case oluşturulmuştur. suite fonksiyonu, bu iki test case’i bir araya getirerek bir test süiti oluşturur. Test süiti, unittest.TextTestRunner nesnesi tarafından çalıştırılır.

  6. Performans Testleri (Performance Tests)Performans testleri, bir uygulamanın yüksek yük altında beklenen performansı sağlayıp sağlamadığını kontrol eder ve Python, bu testleri yazmak için çeşitli kütüphaneler ve araçlar sunar. Özellikle pytest, locust ve pyperf gibi kütüphaneler, uygulamanın performansını ölçmek ve analiz etmek için kullanılır.
    import time
    import pytest
    
    def test_performance():
        start_time = time.time()
        # Some function to test
        function_to_test()
        end_time = time.time()
        assert end_time - start_time < 2
    
Test Verileri ve Fixture’lar

Python ile test işlemlerinde, test verileri testlerin doğru çalışıp çalışmadığını kontrol etmek için oluşturulur ve genellikle bir veritabanında saklanır veya test kodunda tanımlanır. Fixture’lar, testlerin düzgün çalışması için gereken veri veya durumları oluşturur ve Python, pytest kütüphanesi ile bu fixture’ları destekler.

import pytest

@pytest.fixture
def example_data():
    return {
        'username': 'test_user',
        'password': 'test_password'
    }

def test_login(example_data):
    username = example_data['username']
    password = example_data['password']
    assert login(username, password) == True

Bu örnekte, example_data bir fixture’dır ve test_login testi bu fixture’ı kullanır. pytest bu fixture’ı otomatik olarak enjekte eder ve kullanılabilir hale getirir.

Testlerde Veri Tabanı Erişimi ve SQLModel Kullanımı

Test süreçlerinde veri tabanı erişiminin zorlukları, testlerin bağımsız ve yalıtılmış olması gerekliliğinden kaynaklanır. SQLModel, Python’da SQL veri tabanları ile etkileşim için kullanılan bir kütüphane ile bu süreçler gerçekleştirilebilir. Veri tabanı işlemleri genellikle bir transaction içerisinde gerçekleştirilir ve bu, testlerin birbirini etkilememesi için önemlidir.

from sqlmodel import Session, SQLModel, create_engine
from sqlmodel.pool import StaticPool

engine = create_engine("sqlite://", poolclass=StaticPool)

def test_create_customer():
    with Session(engine) as session:
        customer = Customer(name="John Doe", email="john.doe@example.com")
        session.add(customer)
        session.commit()
        assert customer.id is not None

Bu örnekte, create_customer testi bir Customer oluşturup veri tabanına ekler ve ID’nin None olmadığını kontrol eder.

Test Raporlama ve Raporlarının Değerlendirilmesi

Python’da birçok test çerçevesi, otomatik raporlama özellikleri sunar. Örneğin, unittest modülü, testlerin sonuçlarını ve hata mesajlarını otomatik olarak çıktı olarak verir. Bu, test sonuçlarının hızlı ve kolay bir şekilde analiz edilmesini ve problemli alanların belirlenmesini sağlar.

Daha ayrıntılı ve okunabilir test raporları oluşturmak için çeşitli Python kütüphaneleri bulunmaktadır. Bunlardan biri pytest-html kütüphanesidir. pytest-html, pytest test çerçevesi ile birlikte kullanılarak HTML formatında test raporları oluşturur. Bu raporlar, test sonuçlarını, hata mesajlarını ve diğer önemli bilgileri içerir. Bu raporlar, bir web tarayıcısında kolayca görüntülenebilir, bu da raporların paylaşılmasını ve incelenmesini kolaylaştırır.

Test raporlarını okurken ve yorumlarken, öncelikle hatalı testlere odaklanmak genellikle en iyisidir. Hata mesajları, hatalı testlerin neden başarısız olduğuna dair ipuçları sağlar. Bu, hataların hızlı bir şekilde belirlenip düzeltilmesine yardımcı olur. Başarılı testlerin sayısı ve toplam test sayısı, test sürecinin genel başarısını değerlendirmek için kullanılabilir. Bu sayılar, test sürecinin genel etkinliği ve kapsamı hakkında bilgi verir. Testlerin ne kadar sürede çalıştığını gösteren zaman damgaları, performans sorunlarını belirlemeye yardımcı olabilir. Uzun çalışma süreleri, performans sorunlarını gösterebilir ve bu sorunların çözülmesi için daha fazla inceleme gerektirebilir.

CI/CD Süreçleri ve Testlerin Entegrasyonu

CI/CD (Sürekli Entegrasyon / Sürekli Dağıtım) süreçleri, yazılım geliştirme aşamasından itibaren testlerin otomatik olarak entegre edilmesini sağlar, bu da kodun sürekli entegrasyonunu ve dağıtımını mümkün kılar. Her kod değişikliği veya güncelleme ile testler otomatik olarak çalıştırılır, bu da hataların erken tespit edilmesini ve düzeltilmesini sağlar. Araçlar, örneğin Jenkins, Travis CI ve GitLab CI/CD, Python testlerinin otomatik olarak çalıştırılmasını ve sonuçlarının raporlanmasını sağlar. Otomatik testler, CI/CD süreçlerinde yazılımın kalitesini ve güvenilirliğini sağlamak için önemlidir.

Yazımızın sonuna geldik. Test yazarken detaylara dikkat etmek, her yazılım bölümü için ayrıntılı ve kapsamlı testler yapmak daha da önemlidir. Testlerin bağımsız çalıştırılması ve kodun kendini test edebilme yeteneği, sadece bir gereklilik değil, bir zorunluluktur. Testlerin kullanıcı beklentilerini karşılaması ve otomatikleştirilmesi, başarıya giden yolda kritik bir adımdır. Dahası, test stratejileri ve teknikleri sürekli olarak gözden geçirilmeli ve sürekli iyileştirme stratejileri sadece uygulanmalı, aynı zamanda uygulamaya devam etmeliyiz. Sonraki yazıda görüşmek üzere.

Kaynaklar:

Yazar Hakkında
Toplam 9 yazı
Seda KAYADEMİR
Seda KAYADEMİR
Üniversite yıllarında tanışmış olduğu veri dünyasına ilgisi artan ve bu alanda kariyer hedeflemiş bir süre yazılım mühendisi olarak çalışmış ve hedefi doğrultusunda kendini geliştirerek kariyerine veri mühendisi olarak devam etmektedir. İlgilendiği Alanlar; Dataops, Mlops, Bulut Teknolojileri.
Yorumlar (Yorum yapılmamış)

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

×

Bir Şeyler Ara