Come faccio a configurare e smontare correttamente la mia classe pytest con i test?


100

Sto usando il selenio per i test end to end e non riesco a capire come usarlo setup_classe teardown_classmetodi.

Devo impostare il browser nel setup_classmetodo, quindi eseguire una serie di test definiti come metodi di classe e infine chiudere il browser nel teardown_classmetodo.

Ma logicamente sembra una cattiva soluzione, perché in effetti i miei test non funzioneranno con la classe, ma con l'oggetto. Passo selfparam all'interno di ogni metodo di test, quindi posso accedere alle variabili degli oggetti:

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
    

E sembra anche non essere corretto creare un'istanza del browser per la classe .. Dovrebbe essere creato separatamente per ogni oggetto, giusto?

Quindi, devo usare __init__e __del__metodi invece di setup_classe teardown_class?

Risposte:


93

In base alla finalizzazione della Fixture / esecuzione del codice di smontaggio , l'attuale best practice per la configurazione e lo smontaggio consiste nell'usare yieldinvece di 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))

Eseguendolo si ottiene

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

Un altro modo per scrivere il codice di requestsmontaggio è accettare un oggetto -context nella funzione fixture e chiamare il suo request.addfinalizermetodo con una funzione che esegue il teardown una o più volte:

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))

Quindi lo copi in ogni file di test di cui avrai bisogno per la risorsa?
Andy Hayden

@AndyHayden A seconda di come si scrive l'infissi, si potrebbe mettere in ogni file di test in cui è necessario o si potrebbe mettere in un file conftest.py stackoverflow.com/questions/34466027/...
Everett Toews

2
Questa però non è una configurazione di classe, giusto? Viene eseguito prima di ogni metodo di test nella classe.
malhar

1
In questo caso particolare, viene eseguito solo se utilizzato come parametro in un metodo di test. ad esempio il resourceparam intest_that_depends_on_resource(self, resource)
Everett Toews

64

Quando scrivi "test definiti come metodi di classe" , intendi veramente metodi di classe (metodi che ricevono la sua classe come primo parametro) o solo metodi regolari (metodi che ricevono un'istanza come primo parametro)?

Poiché il tuo esempio utilizza selfper i metodi di test, presumo quest'ultimo, quindi devi solo usare setup_methodinvece:

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

L'istanza del metodo di test viene passata a setup_methode teardown_method, ma può essere ignorata se il codice di configurazione / smontaggio non ha bisogno di conoscere il contesto di test. Ulteriori informazioni possono essere trovate qui .

Mi consiglia inoltre di acquisire familiarità con di py.test infissi , in quanto sono un concetto più potente.


1
I dispositivi sono più deboli dei metodi di classe: non consentono la distruzione di oggetti non creati da loro (che è spesso ciò che è veramente necessario). A parte questo, grazie per le informazioni.
wvxvw

Questo mi ha colpito durante l'aggiornamento di una base di codice da una versione 3.0.x di pytest a una variante 4.x. Alcuni vecchi codici usati setup_classcon metodi derisi e simili dovevano essere modernizzati. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

28

Questo potrebbe aiutare http://docs.pytest.org/en/latest/xunit_setup.html

Nella mia suite di test, raggruppo i miei casi di test in classi. Per il setup e lo smontaggio di cui ho bisogno per tutti i casi di test in quella classe, utilizzo i metodi setup_class(cls)e teardown_class(cls)class.

E per la configurazione e lo smontaggio di cui ho bisogno per ciascuno dei casi di test, utilizzo i pulsanti setup_method(method)eteardown_method(methods)

Esempio:

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

Ora, quando eseguo i miei test, quando viene avviata l'esecuzione di TestClass, registra i dettagli per quando inizia l'esecuzione, quando termina l'esecuzione e lo stesso per i metodi.

Puoi aggiungere altri passaggi di configurazione e smontaggio che potresti avere nelle rispettive posizioni.

Spero che sia d'aiuto!


Ciao @Kiran, qual è la differenza tra setup_classvs setup_method?
imsrgadich

1
@imsrgadich Quando organizzi i tuoi casi di test in classi, <setup / teardown> _class viene utilizzato per i passaggi di configurazione e smontaggio della classe e <setup / teardown> _method sono i rispettivi passaggi per ciascun metodo di caso di test.
Kiran Vemuri

1
Dannazione ... ora ho capito! è rimasto bloccato per poche ore. Quindi, per mettere le cose in prospettiva. Il <setup/teardown>_classper tutta la classe. Qui, possono essere cose come l'impostazione del collegamento al DB o il caricamento del file di dati. E poi, ogni caso di test può avere la propria configurazione sotto forma di <setup/teardown>_method. Le cose sono molto chiare adesso. Molte grazie!
imsrgadich

24

Come suggerito da @Bruno, l'utilizzo di dispositivi pytest è un'altra soluzione accessibile per entrambe le classi di test o anche solo per semplici funzioni di test. Ecco un esempio di test delle funzioni di python2.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..."

Quindi, la corsa test_1...produce:

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

Si noti che stuff_i_setupè referenziato nel dispositivo, consentendo a quell'oggetto di essere setupe torn downper il test con cui sta interagendo. Potete immaginare che questo potrebbe essere utile per un oggetto persistente, come un ipotetico database o qualche connessione, che deve essere cancellato prima di eseguire ogni test per mantenerli isolati.


13

Il tuo codice dovrebbe funzionare esattamente come ti aspetti se aggiungi @classmethoddecoratori.

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

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

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


Questo è praticamente esattamente ciò che appare nella documentazione. Il problema che ho avuto con il doc è stato che ho avuto difficoltà a capire il contesto: self è tradizionalmente indicato come self, non cls, quindi mi è sembrato strano, fuori dal contesto della classe stessa. Kiran (sopra) fornisce questo contesto.
Cognitiaclaeves

1
@Cognitiaclaeves "self è tradizionalmente indicato come self, non cls" Sì, selfè usato per i metodi di istanza, dove il primo argomento è l'istanza dell'oggetto specifico su cui si sta svolgendo l'operazione del metodo, mentre clsè usato per @classmethods, che sono vincolati a la classe e non un'istanza della classe (cioè un oggetto).
code_dredd
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.