Jak poprawnie skonfigurować i usunąć moje zajęcia pytest z testami?

112

Używam selenu do testów od końca do końca i nie mogę się dowiedzieć, jak używać setup_classi teardown_classmetod.

Muszę ustawić przeglądarkę w setup_classmetodzie, następnie wykonać kilka testów zdefiniowanych jako metody klasowe i na koniec zamknąć przeglądarkę w teardown_classmetodzie.

Ale logicznie wydaje się to złym rozwiązaniem, ponieważ tak naprawdę moje testy nie będą działać z klasą, ale z obiektem. Przekazuję selfparametr w każdej metodzie testowej, dzięki czemu mam dostęp do zmiennych obiektów:

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
    

I nawet wydaje się, że nie jest poprawne tworzenie instancji przeglądarki dla klasy. Powinno być tworzone osobno dla każdego obiektu, prawda?

Więc muszę użyć __init__i __del__metod zamiast setup_classi teardown_class?

avasin
źródło

Odpowiedzi:

102

Zgodnie z finalizacją / wykonywaniem kodu rozłączania Fixture , obecnie najlepszą praktyką dotyczącą konfiguracji i rozrywania jest użycie yieldzamiast 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))

Uruchomienie tego skutkuje

$ 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 ===

Innym sposobem na napisanie kodu requestdezaktywacji jest zaakceptowanie obiektu -context w funkcji fixture i wywołanie jej request.addfinalizermetody funkcją, która wykonuje rozerwanie raz lub wiele razy:

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))
Everett Toews
źródło
Więc kopiujesz to do każdego pliku testowego, którego będziesz potrzebować?
Andy Hayden
@AndyHayden W zależności od tego, jak piszesz swoje urządzenia, możesz umieścić je w każdym pliku testowym, w którym go potrzebujesz, lub możesz umieścić go w pliku conftest.py stackoverflow.com/questions/34466027/ ...
Everett Toews
2
To jednak nie jest konfiguracja klasowa, prawda? Wykonywałoby się przed każdą metodą testową w klasie.
malhar
1
W tym konkretnym przypadku jest wykonywany tylko wtedy, gdy jest używany jako parametr w metodzie testowej. np. resourceparametr wtest_that_depends_on_resource(self, resource)
Everett Toews
1
Zauważ, że możesz ustawić zakres urządzenia na 'class' i autouse na true, aby upewnić się, że kod jest wywoływany raz dla każdej klasy bez konieczności dołączania go jako parametru do dowolnego wywołania testowego: `` pytest.fixture (scope = " class ", autouse = True) def resource (): print (" setup ") yield" resource "print (" teardown ")` `` `
Chris
68

Pisząc „testy zdefiniowane jako metody klasowe” , czy naprawdę masz na myśli metody klasowe (metody, które otrzymują swoją klasę jako pierwszy parametr), czy tylko zwykłe metody (metody, które otrzymują instancję jako pierwszy parametr)?

Ponieważ w twoim przykładzie są używane selfmetody testowe, zakładam to drugie, więc setup_methodzamiast tego wystarczy użyć :

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

Instancja metody testowej jest przekazywana do setup_methodi teardown_method, ale można ją zignorować, jeśli kod konfiguracji / dezaktywacji nie musi znać kontekstu testowania. Więcej informacji można znaleźć tutaj .

Polecam również zapoznanie się z py.test za uchwyty , ponieważ są one bardziej wydajne pojęcie.

Bruno Oliveira
źródło
1
Urządzenia są słabsze niż metody klasowe: nie pozwalają na niszczenie obiektów, które nie zostały przez nie utworzone (co często jest naprawdę konieczne). Poza tym dziękuję za informacje.
wvxvw
Uderzyło mnie to podczas aktualizacji kodu źródłowego z wersji 3.0.x pytest do wariantu 4.x. Część starszego kodu używanego setup_classz fałszywymi metodami i tym podobnymi wymagała modernizacji. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos
30

Może to pomóc http://docs.pytest.org/en/latest/xunit_setup.html

W moim zestawie testów grupuję przypadki testowe w klasy. Do konfiguracji i rozłączania potrzebnych dla wszystkich przypadków testowych w tej klasie używam metod setup_class(cls)i teardown_class(cls)class.

A do konfiguracji i porzucenia potrzebnego dla każdego przypadku testowego używam setup_method(method)iteardown_method(methods)

Przykład:

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

Teraz, gdy uruchamiam testy, podczas uruchamiania TestClass, rejestruje szczegóły, kiedy zaczyna się wykonywanie, kiedy kończy wykonywanie i to samo dla metod.

Możesz dodać inne kroki konfiguracji i dezaktywacji, które możesz wykonać w odpowiednich lokalizacjach.

Mam nadzieję, że to pomoże!

Kiran Vemuri
źródło
Cześć @Kiran, jaka jest różnica między setup_classvs setup_method?
imsrgadich
1
@imsrgadich Kiedy organizujesz przypadki testowe w klasy, <setup / teardown> _class jest używany do konfiguracji i etapów porzucania klasy, a <setup / teardown> _method to odpowiednie kroki dla każdej metody przypadku testowego.
Kiran Vemuri
1
Cholera ... teraz rozumiem! utknął na nim przez kilka godzin. Tak więc, aby spojrzeć na sprawy z odpowiedniej perspektywy. <setup/teardown>_classDla całej klasy. Tutaj mogą być takie rzeczy, jak ustawienie łącza do bazy danych lub załadowanie pliku danych. A potem każdy przypadek testowy może mieć własną konfigurację w postaci <setup/teardown>_method. Teraz wszystko jest jasne. Wielkie dzięki!
imsrgadich
24

Jak zasugerował @Bruno, użycie urządzeń pytest to kolejne rozwiązanie, które jest dostępne zarówno dla klas testowych, jak i dla prostych funkcji testowych. Oto przykład testowania funkcji Pythona2.7 :

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..."

Tak więc bieganie test_1...daje:

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

Zauważ, że stuff_i_setupistnieje odwołanie w urządzeniu, pozwalając temu obiektowi być setupi torn downna potrzeby testu, z którym współdziała. Można sobie wyobrazić, że może to być przydatne w przypadku trwałego obiektu, takiego jak hipotetyczna baza danych lub jakieś połączenie, które należy wyczyścić przed każdym uruchomieniem testu, aby były izolowane.

ecoe
źródło
14

Twój kod powinien działać tak, jak tego oczekujesz, jeśli dodasz @classmethoddekoratory.

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

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

Zobacz http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/

Okken
źródło
To jest prawie dokładnie to, co pojawia się w dokumentacji. Problem, jaki miałem z tym doktorem, polegał na tym, że miałem trudności ze zrozumieniem kontekstu: ja tradycyjnie określa się jako ja, a nie cls, więc wydawało mi się to dziwne, poza kontekstem samej klasy. Kiran (powyżej) zapewnia ten kontekst.
Cognitiaclaeves
1
@Cognitiaclaeves "self jest tradycyjnie określane jako self, a nie cls" Tak, selfjest używane na przykład metody, gdzie pierwszym argumentem jest konkretna instancja obiektu, na którym ma miejsce operacja metody, podczas gdy clsjest używany do @classmethods, które są klasa, a nie instancja klasy (tj. obiekt).
code_dredd
1
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))

Aby uruchomić:

pytest nam_of_the_module.py -v 
Kiran Sk
źródło