PyTest'in Python'daki iddia beyanı davranışını değiştirmek mümkün mü


18

Gerçek ve beklenen davranışı eşleştirmek için Python iddia ifadeleri kullanıyorum. Bunlar üzerinde test hatası yok gibi bir test yok. Onaylama hatasının kontrolünü ele almak istiyorum ve hata onayında test çantasını iptal etmek isteyip istemediğimi tanımlamak istiyorum.

Ayrıca bir onaylama hatası varsa gibi bir şey eklemek istiyorum o zaman test durumda duraklatılmalı ve kullanıcı her an devam edebilir.

Bunun nasıl yapılacağı hakkında hiçbir fikrim yok

Kod örneği, burada pytest kullanıyoruz

import pytest
def test_abc():
    a = 10
    assert a == 10, "some error message"

Below is my expectation

Assert bir assertionError atar, ben testcase duraklatma seçeneği olmalıdır ve hata ayıklama ve daha sonra devam edebilirsiniz. Duraklatma ve devam ettirmek için tkintermodül kullanacağım . Aşağıdaki gibi bir iddia işlevi yapacağım

import tkinter
import tkinter.messagebox

top = tkinter.Tk()

def _assertCustom(assert_statement, pause_on_fail = 0):
    #assert_statement will be something like: assert a == 10, "Some error"
    #pause_on_fail will be derived from global file where I can change it on runtime
    if pause_on_fail == 1:
        try:
            eval(assert_statement)
        except AssertionError as e:
            tkinter.messagebox.showinfo(e)
            eval (assert_statement)
            #Above is to raise the assertion error again to fail the testcase
    else:
        eval (assert_statement)

İleride bu işlevle her iddia ifadesini değiştirmek zorundayım

import pytest
def test_abc():
    a = 10
    # Suppose some code and below is the assert statement 
    _assertCustom("assert a == 10, 'error message'")

Bu benim için çok fazla çaba harcadığım için iddia ettiğim binlerce yerde değişiklik yapmak zorundayım. Bunu yapmanın kolay bir yolu var mıpytest

Summary:Test çantasını hata durumunda duraklatabildiğim ve hata ayıklamadan sonra devam edebileceğim bir şeye ihtiyacım var. Bunu biliyorum tkinterve bunu kullanmamın nedeni bu. Başka herhangi bir fikir memnuniyetle karşılanacaktır

Note: Yukarıdaki kod henüz test edilmemiştir. Küçük sözdizimi hataları da olabilir

Düzenleme: Cevaplar için teşekkürler. Şimdi bu soruyu biraz ileriye taşıyoruz. Ya iddianın davranışını değiştirmek istersem. Şu anda bir onaylama hatası olduğunda, testcase çıkış yapıyor. Belirli bir onay hatası üzerinde testcase çıkışına ihtiyacım olup olmadığını seçmek istersem ne olur? Yukarıda belirtildiği gibi özel onaylama işlevi yazmak istemiyorum çünkü bu şekilde yer sayısında değişmem gerekiyor


3
Bize ne yapmak istediğinize dair bir kod örneği verebilir misiniz?
mrblewog

1
assertİstediğinizi yapan kendi kontrol işlevlerinizi kullanmayın .
molbdnilo

Neden eklemek Yüklü assert içinde Try blok ve hata mesajının dışında ?
Prathik Kini

1
pytestTest senaryolarınız için gerçekten kullanmak istediğiniz şey gibi geliyor . Test paketlerini yazmayı kolaylaştıran birçok özellik ile birlikte onaylama ve atlama testlerinin kullanılmasını destekler .
blubberdiblub

1
assert cond, "msg"Kodunuzdaki her şeyi mekanik olarak değiştirecek basit bir araç yazmak oldukça kolay olmaz _assertCustom("assert cond, 'msg'")mıydı? Muhtemelen bir sedastar bunu yapabilirdi.
NPE

Yanıtlar:


23

Kullanıyorsunuz pytest, bu da başarısız testlerle etkileşime geçmek için geniş seçenekler sunuyor. Komut satırı seçenekleri ve bunu mümkün kılmak için birkaç kanca sunar. Her birini nasıl kullanacağınızı ve özel hata ayıklama gereksinimlerinize uyacak şekilde özelleştirmeler yapabileceğinizi açıklayacağım.

Ayrıca, gerçekten gerekli olduğunu düşünüyorsanız, belirli iddiaları tamamen atlamanıza izin verecek daha egzotik seçeneklere gideceğim.

İstisnaları ele alın, iddia değil

Başarısız bir testin normalde pytest'i durdurmadığını unutmayın; yalnızca belirli sayıda hatadan sonra çıkmasını açıkça etkinleştirdiyseniz . Ayrıca, istisna oluştuğu için testler başarısız olur; assertyükseltir AssertionErrorama bu en test değil başarısız olmasına neden olacak tek istisna! İstisnaların nasıl değiştirileceğini değil, nasıl ele alınacağını kontrol etmek istersiniz assert.

Ancak, başarısız assert olacak bireysel Testi bitirmek. Çünkü bir istisna bir try...exceptbloğun dışına çıkarıldığında , Python geçerli fonksiyon çerçevesini açar ve buna geri dönüş yoktur.

Bunu istediğinizi düşünmüyorum _assertCustom(), iddiayı yeniden çalıştırma girişimlerinizin açıklamasına bakarak , ama yine de seçeneklerinizi daha ayrıntılı tartışacağım.

PDB ile pytest mortem sonrası hata ayıklama

Bir hata ayıklayıcıdaki hataları işlemek için çeşitli seçenekler için , bir test başarısız olduğunda standart hata ayıklama istemini açan (satır kısaltması için çıktı) --pdbkomut satırı anahtarıyla başlayacağım :

$ mkdir demo
$ touch demo/__init__.py
$ cat << EOF > demo/test_foo.py
> def test_ham():
>     assert 42 == 17
> def test_spam():
>     int("Vikings")
> EOF
$ pytest demo/test_foo.py --pdb
[ ... ]
test_foo.py:2: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(2)test_ham()
-> assert 42 == 17
(Pdb) q
Exit: Quitting debugger
[ ... ]

Bu anahtarla, bir test başarısız olduğunda, protesto mortem sonrası bir hata ayıklama oturumu başlar . Bu aslında tam olarak istediğin şeydi; başarısız bir test noktasındaki kodu durdurmak ve testinizin durumuna bir göz atmak için hata ayıklayıcıyı açmak. Testin yerel değişkenleri, globaller ve yığındaki her karenin yerelleri ve globalleri ile etkileşime girebilirsiniz.

Burada pytest, bu noktadan sonra çıkıp çıkmayacağınız konusunda tam kontrol sağlar: qquit komutunu kullanırsanız, pytest de çalışmadan çıkar, Continue için kullanmak, pytest ciçin kontrolü döndürür ve bir sonraki test yürütülür.

Alternatif bir hata ayıklayıcı kullanma

Bunun için pdbhata ayıklayıcıya bağlı değilsiniz ; --pdbclsanahtarla farklı bir hata ayıklayıcı ayarlayabilirsiniz . IPython hata ayıklayıcı uygulaması veya diğer birçok Python hata ayıklayıcı ( pudb hata ayıklayıcı , anahtarın kullanılmasını veya özel bir eklentiyi gerektirir) dahil olmak üzere herhangi bir pdb.Pdb()uyumlu uygulama çalışır . Anahtar bir modül ve sınıf alır, örneğin kullanmak için:-spudb

$ pytest -s --pdb --pdbcls=pudb.debugger:Debugger

Sen etrafında kendi sarıcı sınıf yazmak için bu özelliği kullanabilirsiniz Pdbo basitçe hemen verir belirli başarısızlık ilgilenen edilir bir şey değilse. pytestKullanımları Pdb()aynen böyle pdb.post_mortem()yapar :

p = Pdb()
p.reset()
p.interaction(None, t)

İşte, tbir geri izleme nesnesi . Zaman p.interaction(None, t)döner, pytestbir sonraki test ile birlikte devam eder, yoksa p.quitting ayarlanır True(nokta pytest sonra terk etme).

Burada test kaldırdı sürece baskılar dışarı biz hemen hatalarının giderilmesi ve döner azalıyorsa o bir örnek uygulamasıdır ValueErrorolarak kaydedilir, demo/custom_pdb.py:

import pdb, sys

class CustomPdb(pdb.Pdb):
    def interaction(self, frame, traceback):
        if sys.last_type is not None and not issubclass(sys.last_type, ValueError):
            print("Sorry, not interested in this failure")
            return
        return super().interaction(frame, traceback)

Ben yukarıdaki demo ile bu kullandığınızda, bu çıktı (yine kısalık için elided):

$ pytest test_foo.py -s --pdb --pdbcls=demo.custom_pdb:CustomPdb
[ ... ]
    def test_ham():
>       assert 42 == 17
E       assert 42 == 17

test_foo.py:2: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Sorry, not interested in this failure
F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../test_foo.py(4)test_spam()
-> int("Vikings")
(Pdb)

Yukarıdaki sys.last_typebaşarısızlığın 'ilginç' olup olmadığını belirlemek için içgözlemler .

Ancak, tkInter veya benzer bir şey kullanarak kendi hata ayıklayıcınızı yazmak istemiyorsanız bu seçeneği gerçekten öneremem. Bunun büyük bir girişim olduğunu unutmayın.

Filtreleme hataları; hata ayıklayıcının ne zaman açılacağını seç ve seç

Bir sonraki seviye, pytest hata ayıklama ve etkileşim kancalarıdır ; bunlar, pytest'in normalde bir istisna işleme veya hata ayıklayıcıya pdb.set_trace()veya breakpoint()(Python 3.7 veya daha yeni) yoluyla girme gibi şeyleri işleme biçimini değiştirmek veya geliştirmek için davranış özelleştirmeleri için kanca noktalarıdır .

Bu kancanın dahili uygulaması, >>> entering PDB >>>yukarıdaki banner'ı da yazdırmaktan sorumludur , bu nedenle hata ayıklayıcının çalışmasını önlemek için bu kancayı kullanmak, bu çıktıyı hiç görmeyeceğiniz anlamına gelir. Bir test arızası 'ilginç' olduğunda kendi kancanıza sahip olabilir ve orijinal kancayı devredebilirsiniz, böylece kullandığınız hata ayıklayıcıdan bağımsız olarak filtre testi hataları ! Dahili uygulamaya adıyla erişerek erişebilirsiniz ; bunun için dahili kanca eklentisi adlandırılır pdbinvoke. Çalışmasını önlemek için kaydını silmeniz gerekir, ancak bir referansı kaydetmeniz gerekir.

İşte böyle bir kancanın örnek bir uygulaması; bunu eklentilerin yüklendiği konumlardan herhangi birine koyabilirsiniz ; Ben koydum demo/conftest.py:

import pytest

@pytest.hookimpl(trylast=True)
def pytest_configure(config):
    # unregister returns the unregistered plugin
    pdbinvoke = config.pluginmanager.unregister(name="pdbinvoke")
    if pdbinvoke is None:
        # no --pdb switch used, no debugging requested
        return
    # get the terminalreporter too, to write to the console
    tr = config.pluginmanager.getplugin("terminalreporter")
    # create or own plugin
    plugin = ExceptionFilter(pdbinvoke, tr)

    # register our plugin, pytest will then start calling our plugin hooks
    config.pluginmanager.register(plugin, "exception_filter")

class ExceptionFilter:
    def __init__(self, pdbinvoke, terminalreporter):
        # provide the same functionality as pdbinvoke
        self.pytest_internalerror = pdbinvoke.pytest_internalerror
        self.orig_exception_interact = pdbinvoke.pytest_exception_interact
        self.tr = terminalreporter

    def pytest_exception_interact(self, node, call, report):
        if not call.excinfo. errisinstance(ValueError):
            self.tr.write_line("Sorry, not interested!")
            return
        return self.orig_exception_interact(node, call, report)

Yukarıdaki eklentisi, iç kullanan TerminalReportereklentisi terminali hatları yazmak için; bu, varsayılan kompakt test durumu formatını kullanırken çıkışı daha temiz hale getirir ve çıkış yakalama etkin olsa bile terminale bir şeyler yazmanıza olanak tanır.

Örnek, eklenti nesnesini pytest_exception_interactbaşka bir kanca aracılığıyla kanca ile kaydeder pytest_configure(), ancak @pytest.hookimpl(trylast=True)dahili pdbinvokeeklentinin kaydını kaldırabilecek kadar geç çalıştığından emin olur . Kanca çağrıldığında, örnek call.exceptinfonesneye karşı test edilir ; ayrıca düğümü veya raporu da kontrol edebilirsiniz .

Yukarıdaki örnek kod yerleştirildiğinde demo/conftest.py, test_hamtest hatası yok sayılır, yalnızca test_spamyükselen test hatası ValueErrorhata ayıklama isteminin açılmasına neden olur:

$ pytest demo/test_foo.py --pdb
[ ... ]
demo/test_foo.py F
Sorry, not interested!

demo/test_foo.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

demo/test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(4)test_spam()
-> int("Vikings")
(Pdb) 

Tekrarlamak gerekirse , yukarıdaki yaklaşımın bunu, pudb veya IPython hata ayıklayıcısı da dahil olmak üzere, pytest ile çalışan herhangi bir hata ayıklayıcı ile birleştirebileceğiniz ek bir avantajı vardır :

$ pytest demo/test_foo.py --pdb --pdbcls=IPython.core.debugger:Pdb
[ ... ]
demo/test_foo.py F
Sorry, not interested!

demo/test_foo.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

demo/test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(4)test_spam()
      1 def test_ham():
      2     assert 42 == 17
      3 def test_spam():
----> 4     int("Vikings")

ipdb>

Ayrıca, hangi testin çalıştırıldığı ( nodeargüman üzerinden ) ve (istisna yoluyla call.excinfo ExceptionInfo) istisnaya doğrudan erişim hakkında çok daha fazla içeriğe sahiptir .

Belirli pytest hata ayıklayıcı eklentilerinin ( pytest-pudbveya pytest-pycharm) kendi pytest_exception_interactkancalarını kaydettiğini unutmayın. Daha eksiksiz bir uygulamanın, eklentileri otomatik olarak geçersiz kılmak config.pluginmanager.list_name_pluginve hasattr()her eklentiyi test etmek için eklenti yöneticisindeki tüm eklentiler arasında döngü yapması gerekir .

Başarısızlıkları tamamen ortadan kaldırmak

Bu, başarısız test hata ayıklaması üzerinde tam kontrol sağlarken, belirli bir test için hata ayıklayıcıyı açmamayı seçmiş olsanız bile , test yine de başarısız olarak kalır. Eğer başarısızlıkları tamamen kaybolması için isterseniz, kullanacağım farklı bir kanca yapabilirsiniz: pytest_runtest_call().

Pytest testleri çalıştırdığında, testi Nonebir istisna döndürmesi veya yükseltmesi beklenen yukarıdaki kanca yoluyla çalıştırır . Buradan bir rapor oluşturulur, isteğe bağlı olarak bir günlük girişi oluşturulur ve test başarısız olursa, yukarıda belirtilen pytest_exception_interact()kanca çağrılır. Tek yapmanız gereken, bu kancanın ürettiği sonucu değiştirmek; bir istisna yerine, hiçbir şey döndürmemelidir.

Bunu yapmanın en iyi yolu bir kanca sargısı kullanmaktır . Kanca sargıları gerçek işi yapmak zorunda değildir, bunun yerine bir kancanın sonucuna ne olduğunu değiştirme şansı verilir. Tek yapmanız gereken satırı eklemektir:

outcome = yield

kanca sarmalayıcı uygulamanızda ve test istisnası da dahil olmak üzere kanca sonucuna erişebilirsiniz outcome.excinfo. Testte bir istisna ortaya çıkarsa, bu öznitelik bir demet (tip, örnek, geri izleme) olarak ayarlanır. Alternatif olarak, outcome.get_result()standart try...exceptişlemeyi arayabilir ve kullanabilirsiniz .

Peki başarısız bir test geçişini nasıl yaparsınız? 3 temel seçeneğiniz vardır:

  • Sarıcıyı arayarak testi beklenen bir hata olarak işaretleyebilirsiniz pytest.xfail().
  • Öğeyi atlanmış olarak işaretleyebilirsiniz, bu da arayarak testin hiçbir zaman ilk sırada yapılmadığını varsayar pytest.skip().
  • outcome.force_result()Yöntemi kullanarak kural dışı durumu kaldırabilirsiniz ; sonucu burada boş bir listeye ayarlayın (yani: kayıtlı kanca hiçbir şey üretmedi None) ve istisna tamamen temizlendi.

Kullandığınız şey size kalmış. Testi başarısız olmuş gibi halletmeniz gerekmediğinden, önce atlanan ve beklenen başarısızlık testlerinin sonucunu kontrol ettiğinizden emin olun. Bu seçeneklerin pytest.skip.Exceptionve aracılığıyla özel istisnalara erişebilirsiniz pytest.xfail.Exception.

Aşağıda ValueError, atlanmadığı gibi yükseltilmeyen başarısız testleri işaretleyen örnek bir uygulama verilmiştir :

import pytest

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
    outcome = yield
    try:
        outcome.get_result()
    except (pytest.xfail.Exception, pytest.skip.Exception, pytest.exit.Exception):
        raise  # already xfailed,  skipped or explicit exit
    except ValueError:
        raise  # not ignoring
    except (pytest.fail.Exception, Exception):
        # turn everything else into a skip
        pytest.skip("[NOTRUN] ignoring everything but ValueError")

Çıktıya konulduğunda conftest.py:

$ pytest -r a demo/test_foo.py
============================= test session starts =============================
platform darwin -- Python 3.8.0, pytest-3.10.0, py-1.7.0, pluggy-0.8.0
rootdir: ..., inifile:
collected 2 items

demo/test_foo.py sF                                                      [100%]

=================================== FAILURES ===================================
__________________________________ test_spam ___________________________________

    def test_spam():
>       int("Vikings")
E       ValueError: invalid literal for int() with base 10: 'Vikings'

demo/test_foo.py:4: ValueError
=========================== short test summary info ============================
FAIL demo/test_foo.py::test_spam
SKIP [1] .../demo/conftest.py:12: [NOTRUN] ignoring everything but ValueError
===================== 1 failed, 1 skipped in 0.07 seconds ======================

-r aBayrağı test_hamşimdi atlanmış olanı daha net hale getirmek için kullandım .

pytest.skip()Aramayı değiştirirseniz, pytest.xfail("[XFAIL] ignoring everything but ValueError")test beklenen bir hata olarak işaretlenir:

[ ... ]
XFAIL demo/test_foo.py::test_ham
  reason: [XFAIL] ignoring everything but ValueError
[ ... ]

ve outcome.force_result([])işaretleri geçerken kullanarak :

$ pytest -v demo/test_foo.py  # verbose to see individual PASSED entries
[ ... ]
demo/test_foo.py::test_ham PASSED                                        [ 50%]

Hangisinin kullanım durumunuza en uygun olduğunu düşünüyorsunuz. İçin skip()ve xfail()standart ileti biçimini taklit ettim ( [NOTRUN]veya ön ekine sahip [XFAIL]), ancak istediğiniz diğer ileti biçimini kullanmakta serbestsiniz.

Her üç durumda da pytest, sonucunu bu yöntemi kullanarak değiştirdiğiniz testler için hata ayıklayıcıyı açmayacaktır.

Bireysel iddia beyanlarını değiştirme

Bir asserttest içindeki testleri değiştirmek istiyorsanız, kendinizi çok daha fazla iş için hazırlıyorsunuz. Evet, bu teknik olarak mümkündür, ancak yalnızca Python'un derleme zamanında yürüteceği kodu yeniden yazarak .

Kullandığınızda pytest, bu aslında zaten yapılıyor . Pytest, açıklamalarınız başarısız olduğunda size daha fazla bağlam sağlamak için ifadeleri yeniden yazarassert ; kaynak kodunun yanı sıra tam olarak ne yapıldığına ilişkin iyi bir genel bakış için bu blog yayınına bakın . Bu modülün 1k satırdan uzun olduğunu ve Python'un soyut sözdizimi ağaçlarının nasıl çalıştığını anlamanız gerektiğini unutmayın . Eğer yaparsanız, olabilir çevreleyen dahil Orada kendi değişiklikler eklemek için bu modülü monkeypatch bir ile işleyici._pytest/assertion/rewrite.pyasserttry...except AssertionError:

Ancak , sonraki ifadeler kolayca atlanan bir vericinin karşı koymak istediği duruma (belirli nesne düzenlemeleri, değişkenler vb.) Bağlı olabileceğinden, seçimleri yalnızca seçerek devre dışı bırakamaz veya yok sayamazsınız. Bir assert testlerinde ise fooedilmez None, daha sonra bir sonraki assert dayanıyor foo.barvaroldukları için, sonra sadece bir içine çalışacaktır AttributeErrorbu yolu gitmek gerekiyorsa, yeniden yükselterek istisna vb orada Do sopa.

assertsBurada yeniden yazma konusunda daha fazla ayrıntıya girmeyeceğim , çünkü bunun çalışmaya değer olduğunu düşünmüyorum, dahil edilen iş miktarı göz önüne alınmadı ve ölüm sonrası hata ayıklama ile testin durumuna erişmenizi sağlayın yine de iddia noktası başarısızlığı .

Bunu yapmak istiyorsanız, eval()(yine de işe yaramaz assert, bir deyimdir, bu yüzden exec()bunun yerine kullanmanız gerekir ) kullanmanıza gerek yoktur ve ayrıca iddiayı iki kez çalıştırmanız gerekmez ( iddianın değiştirilme durumunda kullanıldığı ifade kullanılırsa sorunlara yol açabilir). Bunun yerine ast.Assertdüğümü bir düğümün içine gömer ve ast.Tryboş bir ast.Raisedüğüm kullanan bir dış işleyici eklersiniz ve yakalanan özel durumu yeniden yükseltirsiniz.

Onaylama ifadelerini atlamak için hata ayıklayıcıyı kullanma.

Python hata ayıklayıcısı aslında / komutunu kullanarak ifadeleri atlamanızı sağlar . Eğer biliyorsanız ön yukarı belirli bir iddiası olduğunu edecektir başarısız, bunu baypas için kullanabilir. Her bir testin başlangıcında hata ayıklayıcıyı açan testlerinizi şu şekilde çalıştırabilir , ardından hata ayıklayıcı onaylandıktan hemen önce duraklatıldığında atlamak için bir a verebilirsiniz.jjump--tracej <line after assert>

Hatta bunu otomatikleştirebilirsiniz. Yukarıdaki teknikleri kullanarak özel bir hata ayıklayıcı eklentisi oluşturabilirsiniz.

  • istisnayı pytest_testrun_call()yakalamak için kancayı kullanırAssertionError
  • satırdaki 'rahatsız edici' satır numarasını izlemeden çıkarır ve belki de bazı kaynak kod analizi ile başarılı bir atlama gerçekleştirmek için gereken onaylamadan önce ve sonra satır numaralarını belirler
  • testi tekrar çalıştırır , ancak bu kez Pdb, onaylamadan önceki satırda bir kesme noktası belirleyen ve kesme noktası vurulduğunda otomatik olarak ikinci bir atlama gerçekleştiren bir alt sınıf kullanarak cdevam eder.

Ya da bir iddianın başarısız olmasını beklemek yerine, assertbir testte bulunan her biri için kesme noktalarını ayarlamayı otomatik hale getirebilirsiniz (yine kaynak kod analizini kullanarak, testin bir AST'sindeki ast.Assertdüğümler için önemsiz bir şekilde satır numaralarını çıkarabilirsiniz ), test edilen testi yürütebilirsiniz hata ayıklayıcı komut dosyası komutlarını kullanın jumpve onayın kendisini atlamak için komutu kullanın. Bir ödün vermek zorunda kalacaksınız; bir hata ayıklayıcı altında tüm testleri çalıştırın (yorumlayıcı her deyim için bir izleme işlevini çağırması gerektiği için yavaştır) veya bunu yalnızca başarısız testlere uygular ve bu testleri sıfırdan tekrar çalıştırmanın bedelini öder.

Böyle bir eklenti oluşturmak için çok iş olurdu, burada bir örnek yazmayacağım, kısmen bir cevaba uymayacağı için ve kısmen de buna değdiğini düşünmediğim için . Hata ayıklayıcıyı açıp atlamayı elle yapardım. Başarısız bir iddia, testin kendisinde veya test altındaki kodda bir hata olduğunu gösterir; bu nedenle, sorunu ayıklamaya odaklanabilirsiniz.


7

Pytest --pdb ile herhangi bir kod değişikliği yapmadan tam olarak istediğinizi elde edebilirsiniz .

Örneğinizle:

import pytest
def test_abc():
    a = 9
    assert a == 10, "some error message"

--Pdb ile çalıştır:

py.test --pdb
collected 1 item

test_abc.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_abc():
        a = 9
>       assert a == 10, "some error message"
E       AssertionError: some error message
E       assert 9 == 10

test_abc.py:4: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /private/tmp/a/test_abc.py(4)test_abc()
-> assert a == 10, "some error message"
(Pdb) p a
9
(Pdb)

Test başarısız olur olmaz, yerleşik python hata ayıklayıcı ile hata ayıklayabilirsiniz. Hata ayıklamayı bitirdiyseniz, continuetestlerin geri kalanıyla yapabilirsiniz .


Test durumu başarısız olduğunda veya test adımı başarısız olduğunda bu durur mu?
Nitesh

Lütfen bağlantılı dokümanları kontrol edin: doc.pytest.org/en/latest/…
gnvk

Mükemmel fikir. Ancak --pdb kullanılırsa, testcase her hatada duraklar. test davasını duraklatmak istediğim zamanda karar verebilir miyim
Nitesh

5

PyCharm kullanıyorsanız, bir onaylama başarısız olduğunda yürütmeyi duraklatmak için bir İstisna Kesme Noktası ekleyebilirsiniz. Kesme Noktalarını Görüntüle'yi (CTRL-SHIFT-F8) seçin ve AssertionError için yükseltilmiş bir istisna işleyici ekleyin. Bunun testlerin yürütülmesini yavaşlatabileceğini unutmayın.

Aksi takdirde, iddianın başarısız olduğu noktadan ziyade her başarısız testin sonunda (hatalardan hemen önce) duraklamayı önemsemiyorsanız , birkaç seçeneğiniz vardır. Bununla birlikte, bu noktaya kadar, testte açılan kapatma dosyaları gibi çeşitli temizleme kodlarının zaten çalıştırılmış olabileceğini unutmayın. Olası seçenekler şunlardır:

  1. --Pdb seçeneğini kullanarak pytest'e hatalarda hata ayıklayıcının içine düşmesini söyleyebilirsiniz .

  2. Aşağıdaki dekoratörü tanımlayabilir ve ilgili her test fonksiyonunu onunla dekore edebilirsiniz. (Dışında bir ileti günlüğe kaydetmesini, ayrıca bir başlayabileceğini pdb.post_mortem bu noktada, hatta interaktif code.interact açıklandığı gibi istisna, kökenli çerçevenin halk ile bu cevap .)

from functools import wraps

def pause_on_assert(test_func):
    @wraps(test_func)
    def test_wrapper(*args, **kwargs):
        try:
            test_func(*args, **kwargs)
        except AssertionError as e:
            tkinter.messagebox.showinfo(e)
            # re-raise exception to make the test fail
            raise
    return test_wrapper

@pause_on_assert
def test_abc()
    a = 10
    assert a == 2, "some error message"
  1. El her test fonksiyonunun süslemek için istemiyorsanız, bunun yerine denetimler yaptığı bir autouse fikstürü tanımlayabilirsiniz sys.last_value :
import sys

@pytest.fixture(scope="function", autouse=True)
def pause_on_assert():
    yield
    if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
        tkinter.messagebox.showinfo(sys.last_value)

Dekoratörler ile cevap hoşuma gitti ama dinamik olarak yapılamaz. Pause_on_assert ya da istemiyorum zaman dinamik olarak kontrol etmek istiyorum. Bunun için herhangi bir çözüm var mı?
Nitesh

Hangi açıdan dinamik? Her yerde etkinleştirmek / devre dışı bırakmak için tek bir anahtarda olduğu gibi? Veya her test için kontrol etmenin bir yolu mu?
Uri Granta

Diyelim ki bazı testcasları çalıştırıyorum. Ortada başarısızlık için duraklama ihtiyacım var. Anahtarı etkinleştireceğim. Daha sonra herhangi bir noktada anahtarı devre dışı bırakmam gerektiğini hissediyorum.
Nitesh

Dekoratörünüz cevapta: 2 benim için çalışmaz çünkü test durumumda birden fazla öneri olacak
Nitesh

Bir 'anahtar' ile ilgili olarak, pause_on_assertduraklatıp duraklatmamaya karar vermek için bir dosyadan okunacak uygulamayı güncelleyebilirsiniz .
Uri Granta

4

Visual Studio Code kullanmaya hazırsanız, basit bir çözüm koşullu kesme noktaları kullanmak olabilir .

Bu, iddialarınızı ayarlamanıza olanak tanır, örneğin:

import pytest
def test_abc():
    a = 10
    assert a == 10, "some error message"

Ardından, onay satırınıza yalnızca onaylamanız başarısız olduğunda kesilecek koşullu bir kesme noktası ekleyin:

resim açıklamasını buraya girin


@Nitesh - Bence bu çözüm tüm sorunlarınızı çözüyor, sadece bir iddianın başarısız olması durumunda kırılıyor, kodun hatalarını ayıklayabilir ve daha sonra kalan testlere devam edebilirsiniz ... Yine de kurmak biraz daha zahmetli başlangıçta
Nick Martin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.