Pytest ile ilgili temel bilgiler

Tamamlandı

Pytest ile teste başlayalım. Önceki ünitede de belirttiğimiz gibi Pytest yüksek oranda yapılandırılabilir ve karmaşık test paketlerini işleyebilir, ancak test yazmaya başlamak için çok fazla işlem gerektirmez. Aslında, bir çerçeve test yazmanızı ne kadar kolay sağlarsa o kadar iyidir.

Bu bölümün sonunda, ilk testlerinizi yazmaya başlamak ve Pytest ile çalıştırmak için ihtiyacınız olan her şeye sahip olmanız gerekir.

Kurallar

Yazma testlerine başlamadan önce Pytest'in bağlı olduğu bazı test kurallarını ele almalıyız.

Python'da test dosyaları, test dizinleri veya genel test düzenleri hakkında çok fazla sabit kural yoktur. Bu kuralları bilerek, ek yapılandırmaya gerek kalmadan otomatik test bulma ve yürütme avantajlarından yararlanabilirsiniz.

Dizin ve test dosyalarını test etme

Testler için ana dizin, test dizinidir. Bu dizini projenin kök düzeyine yerleştirebilirsiniz, ancak kod modülleriyle birlikte görmek de olağan dışı değildir.

Not

Bu modülde, varsayılan olarak projenin kökündeki testleri kullanacağız.

Şimdi adlı jformat küçük bir Python projesinin kökünün nasıl göründüğüne bakalım:

.
├── README.md
├── jformat
│   ├── __init__.py
│   └── main.py
├── setup.py
└── tests
    └── test_main.py

Testler dizini, tek bir test dosyasıyla projenin kökündedir. Bu durumda, test dosyası test_main.py olarak adlandırılır. Bu örnekte iki kritik kural gösterilmektedir:

  • Test dosyalarını ve iç içe test dizinlerini yerleştirmek için test dizini kullanın.
  • Test ile test dosyalarına ön ek oluşturun. Ön ek, dosyanın test kodu içerdiğini gösterir.

Dikkat

Dizin adı olarak (tekil form) kullanmaktan test kaçının. Ad test bir Python modülü olduğundan aynı adlı bir dizin oluşturmak bunu geçersiz kılar. Bunun yerine her zaman çoğul tests kullanın.

İşlevleri test etme

Pytest'i kullanmanın güçlü bir nedeni, test fonksiyonları yazmanıza olanak sağlamasıdır. Test dosyalarına benzer şekilde, test işlevlerine ön ek test_eklenmelidir. Ön ek, test_ Pytest'in testi toplamasını ve yürütmesini sağlar.

Basit bir test işlevi şöyle görünür:

def test_main():
    assert "a string value" == "a string value"

Not

Eğer unittest ile ilgili bilginiz varsa, test işlevinde assert kullanımını görmek şaşırtıcı olabilir. Düz onayları daha sonra daha ayrıntılı olarak ele alacağız, ancak Pytest ile düz onaylarla zengin hata raporlaması elde edersiniz.

Test sınıfları ve test yöntemleri

Dosya ve işlevlere yönelik kurallar gibi test sınıfları ve test yöntemleri de aşağıdaki kuralları kullanır:

  • Test sınıflarının ön ekleri Test
  • Test yöntemlerine ön ek eklendi test_

Python unittest kitaplığının temel bir farkı devralma gerek olmamasıdır.

Aşağıdaki örnek, sınıflar ve yöntemler için bu ön ekleri ve diğer Python adlandırma kurallarını kullanır. Uygulamadaki kullanıcı adlarını denetleen küçük bir test sınıfı gösterir.

class TestUser:

    def test_username(self):
        assert default() == "default username"

Testleri çalıştırma

Pytest hem bir test çerçevesi hem de test çalıştırıcısıdır. Test çalıştırıcısı, komut satırında bulunan ve (üst düzeyde) şu komutu çalıştırabilen bir yürütülebilir dosyadır:

  • Bir test çalıştırması için tüm test dosyalarını, test sınıflarını ve test işlevlerini bularak test koleksiyonunu gerçekleştirin.
  • Tüm testleri yürüterek bir test çalıştırması başlatın.
  • Hataları, hataları ve geçiş testlerini takip edin.
  • Test çalıştırmasının sonunda zengin raporlama sağlar.

Not

Pytest bir dış kitaplık olduğundan, bunu kullanabilmek için yüklenmesi gerekir .

test_main.py dosyasındaki bu içerikler göz önünde bulundurulduğunda Pytest'in testleri çalıştırırken nasıl davrandığını görebiliriz:

# contents of test_main.py file

def test_main():
    assert True

Komut satırında, test_main.py dosyasının

 $ pytest
=========================== test session starts ============================
platform -- Python 3.10.1, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /private/tmp/project
collected 1 item

test_main.py .                                                       [100%]

============================ 1 passed in 0.00s =============================

Pytest, örnek testi arka planda herhangi bir yapılandırma gerekmeden test dosyasında toplar.

Güçlü assert deyimi

Şu ana kadar, test örneklerimizin tümü düz assert çağrıyı kullanıyor. Genellikle Python'da assert deyimi testler için kullanılmaz çünkü onaylama işlemi başarısız olduğunda düzgün raporlama özelliği yoktur. Ancak Pytest'te bu sınırlama yoktur. Arka planda Pytest, kullanıcıyı daha fazla kod yazmaya veya herhangi bir şeyi yapılandırmaya zorlamadan ifadelerin zengin karşılaştırmalar gerçekleştirmesini mümkün kılar.

düz assert deyimini kullanarak Python işleçlerini kullanabilirsiniz; örneğin, >, <, !=, >=veya <=. Python'ın tüm işleçleri geçerlidir. Bu özellik Pytest'in en önemli tek özelliği olabilir: Onayları yazmak için yeni söz dizimi öğrenmeniz gerekmez.

Python nesneleriyle yaygın karşılaştırmalarla ilgilenirken bunun nasıl çevrildiğini görelim. Bu durumda, uzun dizeleri karşılaştırırken hata raporunu inceleyelim:

================================= FAILURES =================================
____________________________ test_long_strings _____________________________

    def test_long_strings():
        left = "this is a very long strings to be compared with another long string"
        right = "This is a very long string to be compared with another long string"
>       assert left == right
E       AssertionError: assert 'this is a ve...r long string' == 'This is a ve...r long string'
E         - This is a very long string to be compared with another long string
E         ? ^
E         + this is a very long strings to be compared with another long string
E         ? ^                         +

test_main.py:4: AssertionError

Pytest, hatayla ilgili yararlı bir bağlam sunar: dizenin başında yanlış bir büyük harf kullanımı ve bir sözcüğün içine ek bir karakter. Ancak Pytest, dizelerin ötesinde diğer nesneler ve veri yapılarında yardımcı olabilir. Örneğin, listelerle şu şekilde davranır:

________________________________ test_lists ________________________________

    def test_lists():
        left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
        right = ["sugar", "coffee", "wheat", "salt", "water", "milk"]
>       assert left == right
E       AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'co...ater', 'milk']
E         At index 1 diff: 'wheat' != 'coffee'
E         Full diff:
E         - ['sugar', 'coffee', 'wheat', 'salt', 'water', 'milk']
E         ?                     ---------
E         + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E         ?           +++++++++

test_main.py:9: AssertionError

Bu rapor, dizin 1'in (listedeki ikinci öğe) farklı olduğunu tanımlar. Yalnızca dizin numarasını tanımlamaz, aynı zamanda hatanın bir gösterimini de sağlar. Öğe karşılaştırmalarının yanı sıra, öğelerin eksik olup olmadığını da raporlayabilir ve size tam olarak hangi öğenin bulunabileceğini bildiren bilgiler sağlayabilir. Aşağıdaki örnekte:"milk"

________________________________ test_lists ________________________________

    def test_lists():
        left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
        right = ["sugar", "wheat", "salt", "water", "milk"]
>       assert left == right
E       AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'wh...ater', 'milk']
E         At index 2 diff: 'coffee' != 'salt'
E         Left contains one more item: 'milk'
E         Full diff:
E         - ['sugar', 'wheat', 'salt', 'water', 'milk']
E         + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E         ?                    ++++++++++

test_main.py:9: AssertionError

Son olarak sözlüklerle nasıl davrandığını görelim. Hatalar varsa iki büyük sözlük karşılaştırması zor olabilir, ancak Pytest bağlam sağlama ve hatayı belirleme konusunda olağanüstü bir iş yapar:

____________________________ test_dictionaries _____________________________

    def test_dictionaries():
        left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
        right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
>       assert left == right
E       AssertionError: assert {'county': 'F...rry Ln.', ...} == {'county': 'F...ry Lane', ...}
E         Omitting 3 identical items, use -vv to show
E         Differing items:
E         {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E         {'number': 39} != {'number': 38}
E         Full diff:
E           {
E            'county': 'Frett',...
E
E         ...Full output truncated (12 lines hidden), use '-vv' to show

Bu testte sözlükte iki hata vardır. Biri değerin "street" farklı olması, diğeri ise eşleşmeyen değerdir "number" .

Pytest bu farklılıkları doğru bir şekilde algılar (tek bir testte tek bir hata olsa bile). Sözlükler birçok öğe içerdiğinden Pytest aynı bölümleri atlar ve yalnızca ilgili içeriği gösterir. Çıkıştaki ayrıntı düzeyini artırmak için önerilen -vv bayrağı kullanırsak ne olacağını görelim:

____________________________ test_dictionaries _____________________________

    def test_dictionaries():
        left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
        right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
>       assert left == right
E       AssertionError: assert {'county': 'Frett',\n 'number': 39,\n 'state': 'Nevada',\n 'street': 'Ferry Ln.',\n 'zipcode': 30877} == {'county': 'Frett',\n 'number': 38,\n 'state': 'Nevada',\n 'street': 'Ferry Lane',\n 'zipcode': 30877}
E         Common items:
E         {'county': 'Frett', 'state': 'Nevada', 'zipcode': 30877}
E         Differing items:
E         {'number': 39} != {'number': 38}
E         {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E         Full diff:
E           {
E            'county': 'Frett',
E         -  'number': 38,
E         ?             ^
E         +  'number': 39,
E         ?             ^
E            'state': 'Nevada',
E         -  'street': 'Ferry Lane',
E         ?                    - ^
E         +  'street': 'Ferry Ln.',
E         ?                     ^
E            'zipcode': 30877,
E           }

komutunu çalıştırarak pytest -vvraporlama, ayrıntı miktarını artırır ve ayrıntılı bir karşılaştırma sağlar. Bu rapor yalnızca hatayı algılayıp göstermekle kalmaz, aynı zamanda sorunu düzeltmek için hızlı bir şekilde değişiklik yapmanıza da olanak tanır.