Testlerle pytest sınıfım için nasıl doğru bir şekilde kurulum ve sökme yapabilirim?


112

Uçtan uca test için selenyum kullanıyorum setup_classve nasıl kullanılacağını ve teardown_classyöntemlerini alamıyorum .

setup_classYöntemde tarayıcıyı kurmam, ardından sınıf yöntemleri olarak tanımlanan bir dizi test gerçekleştirmem ve son olarak yöntemde tarayıcıdan çıkmam gerekiyor teardown_class.

Ancak mantıksal olarak bu kötü bir çözüm gibi görünüyor, çünkü aslında benim testlerim sınıfla değil, nesneyle çalışacak. Ben geçmek selfI değişkenler nesnelere erişebilir, böylece her bir test yöntemi içinde param:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

Hatta sınıf için tarayıcı örneği oluşturmak bile doğru görünmüyor .. Her nesne için ayrı ayrı oluşturulmalı, değil mi?

Yani, kullanmak gerekir __init__ve __del__yerine yöntemleri setup_classve teardown_class?

Yanıtlar:


102

Fikstür sonlandırmasına / sökme kodunu yürütmeye göre , kurulum ve sökme için mevcut en iyi uygulama şunun yieldyerine kullanmaktır return:

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Çalıştırmak

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

requestSökme kodu yazmanın başka bir yolu , fikstür işlevinize bir -bağlam nesnesini kabul etmek ve bunun request.addfinalizeryöntemini bir veya birden çok kez ayırma gerçekleştiren bir işlevle çağırmaktır :

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Yani bunu kaynağa ihtiyaç duyacağınız her test dosyasına kopyalıyorsunuz?
Andy Hayden

@AndyHayden Fikstürlerinizi nasıl yazdığınıza bağlı olarak, onu ihtiyaç duyduğunuz her test dosyasına koyabilir veya bir conftest.py dosyasına koyabilirsiniz stackoverflow.com/questions/34466027/…
Everett Toews

2
Ancak bu bir sınıf düzeni değil, değil mi? Sınıftaki her test yönteminden önce çalıştırılır.
malhar

1
Bu özel durumda, yalnızca bir test yönteminde bir parametre olarak kullanıldığında yürütülür. ör. resourceparamtest_that_depends_on_resource(self, resource)
Everett Toews

1
Kodun herhangi bir test çağrısına parametre olarak dahil etmek zorunda kalmadan her sınıf için bir kez çağrıldığından emin olmak için fikstür kapsamını 'sınıf' ve otomatik kullanımı true olarak ayarlayabileceğinizi unutmayın: `` pytest.fixture (kapsam = " class ", autouse = True) def resource (): print (" setup ")" resource "print (" teardown ")` `verimi
Chris,

68

"Sınıf yöntemleri olarak tanımlanan testleri" yazdığınızda , gerçekten sınıf yöntemlerini mi ( sınıfını ilk parametre olarak alan yöntemler ) mi yoksa yalnızca normal yöntemleri mi (bir örneği ilk parametre olarak alan yöntemler ) mi kastediyorsunuz ?

selfÖrneğiniz test yöntemleri için kullandığından , ikincisini varsayıyorum, bu yüzden setup_methodbunun yerine kullanmanız gerekir :

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

Test yöntemi örneği setup_methodve öğesine iletilir teardown_method, ancak kurulum / sökme kodunuzun test bağlamını bilmesi gerekmiyorsa yok sayılabilir. Daha fazla bilgiyi burada bulabilirsiniz .

Ayrıca py.test'in armatürlerini daha güçlü bir konsept oldukları için tanımanızı tavsiye ederim .


1
Fikstürler, sınıf yöntemlerinden daha zayıftır: kendileri tarafından yaratılmayan nesnelerin yok edilmesine izin vermezler (bu genellikle gerçekten gerekli olan şeydir). Bunun dışında bilgi için teşekkür ederim.
wvxvw

Bu, bir kod tabanını pytest'in 3.0.x sürümünden 4.x değişkenine yükseltirken beni vurdu. setup_classAlay yöntemleriyle kullanılan bazı eski kodlar ve benzerlerinin modernize edilmesi gerekiyordu. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

30

Bu, http://docs.pytest.org/en/latest/xunit_setup.html yardımcı olabilir.

Test paketimde, test durumlarımı sınıflara ayırıyorum. Kurulum ve sökme için, o sınıftaki tüm test durumları için ihtiyacım olan setup_class(cls)ve sınıf yöntemlerini kullanıyorum teardown_class(cls).

Kurulum ve sökme işlemi için her bir test senaryosunda ihtiyacım olan setup_method(method)veteardown_method(methods)

Misal:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Şimdi, testlerimi çalıştırdığımda, TestClass yürütmesi başladığında, yürütmeye ne zaman başladığına, yürütmeyi ne zaman bitirdiğine ilişkin ayrıntıları günlüğe kaydeder ve yöntemler için aynıdır ..

İlgili konumlarda sahip olabileceğiniz diğer kurulum ve sökme adımlarını ekleyebilirsiniz.

Umarım yardımcı olur!


Merhaba @Kiran, setup_classvs arasındaki fark setup_methodnedir?
imsrgadich

1
@imsrgadich Test olaylarınızı sınıflar halinde düzenlediğinizde, sınıfın kurulum ve sökme adımları için <setup / teardown> _class kullanılır ve her test senaryosu yöntemi için ilgili adımlar <setup / teardown> _method'dur.
Kiran Vemuri

1
Kahretsin ... şimdi anladım! birkaç saat boyunca takılı kaldı. Yani, işleri perspektif içine koymak için. <setup/teardown>_classBütün sınıf için. Burada, DB'ye bağlantı kurmak veya veri dosyasını yüklemek gibi şeyler olabilir. Ve sonra, her test senaryosu şeklinde kendi kurulumuna sahip olabilir <setup/teardown>_method. Artık işler çok net. Çok teşekkürler!
imsrgadich

24

@Bruno'nun önerdiği gibi, pytest armatürlerini kullanmak, hem test sınıfı hem de sadece basit test fonksiyonları için erişilebilen başka bir çözümdür. İşte python2.7 işlevlerini test eden bir örnek :

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

Yani, koşmak test_1...şunları üretir:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Fikstürde stuff_i_setupreferans verilen, o nesnenin olmasına setupve torn downetkileşime girdiği test için izin verilene dikkat edin. Bunun, her test çalıştırılmadan önce onları izole tutmak için temizlenmesi gereken varsayımsal bir veritabanı veya bazı bağlantılar gibi kalıcı bir nesne için faydalı olabileceğini hayal edebilirsiniz.


14

@classmethodDekoratörler eklerseniz, kodunuz beklediğiniz gibi çalışmalıdır .

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

Bkz. Http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/


Bu, hemen hemen belgelerde görünen şeydir. Dokümanla yaşadığım sorun, bağlamı anlamakta güçlük çekiyor olmamdı: benlik geleneksel olarak cls değil, benlik olarak adlandırılıyor, bu yüzden bu bana sınıfın bağlamı dışında garip geldi. Kiran (yukarıda) bu bağlamı sağlar.
Cognitiaclaeves

1
@Cognitiaclaeves "self, geleneksel olarak cls değil, self olarak adlandırılır" Evet, selförneğin yöntemlerde kullanılır; burada ilk argüman, yöntem işleminin gerçekleştiği belirli nesne örneğidir ve s clsiçin kullanılır @classmethod; sınıf, sınıfın bir örneği değil (yani bir nesne).
code_dredd

2
import pytest
class Test:
    @pytest.fixture()
    def setUp(self):
        print("setup")
        yield "resource"
        print("teardown")

    def test_that_depends_on_resource(self, setUp):
        print("testing {}".format(setUp))

Çalıştırmak için:

pytest nam_of_the_module.py -v 
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.