Como configuro e desmonto corretamente minha classe pytest com testes?


100

Estou usando selênio para testes ponta a ponta e não consigo descobrir como usar setup_classe teardown_classmétodos.

Preciso configurar o navegador no setup_classmétodo, realizar vários testes definidos como métodos de classe e, finalmente, sair do navegador no teardown_classmétodo.

Mas logicamente parece uma solução ruim, pois na verdade meus testes não funcionarão com classe, mas com objeto. Eu passo o selfparam dentro de cada método de teste, para poder acessar os vars dos objetos:

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 até parece não ser correto criar instância de navegador para classe .. Deve ser criado para cada objeto separadamente, certo?

Portanto, preciso usar os métodos __init__e em __del__vez de setup_classe teardown_class?

Respostas:


93

De acordo com o código de finalização / execução do dispositivo de fixação , a prática recomendada atual para configuração e desmontagem é usar em yieldvez de 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))

Executá-lo resulta em

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

Outra forma de escrever código de desmontagem é aceitar um requestobjeto -context em sua função de fixação e chamar seu request.addfinalizermétodo com uma função que executa a desmontagem uma ou várias vezes:

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

Então, você copia isso em todos os arquivos de teste dos quais vai precisar do recurso?
Andy Hayden

@AndyHayden Dependendo de como você escreve seus acessórios, você pode colocá-los em cada arquivo de teste onde precisar ou pode colocá-los em um arquivo conftest.py stackoverflow.com/questions/34466027/…
Everett Toews

2
No entanto, isso não é uma configuração de classe, certo? Ele seria executado antes de cada método de teste da classe.
Malhar

1
Nesse caso específico, ele só é executado quando usado como parâmetro em um método de teste. por exemplo, o resourceparâmetro emtest_that_depends_on_resource(self, resource)
Everett Toews

64

Quando você escreve "testes definidos como métodos de classe" , você realmente quer dizer métodos de classe (métodos que recebem sua classe como primeiro parâmetro) ou apenas métodos regulares (métodos que recebem uma instância como primeiro parâmetro)?

Como seu exemplo usa selfpara os métodos de teste, estou assumindo o último, então você só precisa usar setup_method:

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

A instância do método de teste é passada para setup_methode teardown_method, mas pode ser ignorada se seu código de configuração / desmontagem não precisar conhecer o contexto de teste. Mais informações podem ser encontradas aqui .

Também recomendo que você se familiarize com os acessórios do py.test , pois são um conceito mais poderoso.


1
As luminárias são mais fracas do que os métodos de classe: elas não permitem a destruição de objetos não criados por elas (o que geralmente é o que é realmente necessário). Fora isso, obrigado pela informação.
wvxvw

Isso me atingiu durante a atualização de uma base de código de uma versão 3.0.x do pytest para uma variante 4.x. Alguns códigos mais antigos usados setup_classcom métodos simulados e semelhantes precisavam ser modernizados. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

28

Isso pode ajudar http://docs.pytest.org/en/latest/xunit_setup.html

Em minha suíte de teste, eu agrupo meus casos de teste em classes. Para a configuração e a desmontagem de que preciso para todos os casos de teste dessa classe, uso os métodos de classe setup_class(cls)e teardown_class(cls).

E para a configuração e desmontagem que preciso para cada caso de teste, uso o setup_method(method)eteardown_method(methods)

Exemplo:

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

Agora quando eu executo meus testes, quando a execução do TestClass está começando, ele registra os detalhes de quando está começando a execução, quando está terminando a execução e o mesmo para os métodos.

Você pode adicionar outras etapas de configuração e desmontagem que possa ter nos respectivos locais.

Espero que ajude!


Olá @Kiran, qual é a diferença entre o setup_classvs setup_method?
imsrgadich

1
@imsrgadich Quando você organiza seus casos de teste em classes, <setup / teardown> _class é usado para as etapas de configuração e desmontagem da classe e <setup / teardown> _method são as respectivas etapas para cada método de caso de teste.
Kiran Vemuri

1
Droga ... agora entendi! ficou preso nele por algumas horas. Então, para colocar as coisas em perspectiva. O <setup/teardown>_classpara toda a classe. Aqui, podem ser coisas como definir o link para o banco de dados ou carregar o arquivo de dados. E então, cada caso de teste pode ter sua própria configuração na forma de <setup/teardown>_method. As coisas estão muito claras agora. Muito obrigado!
imsrgadich

24

Como @Bruno sugeriu, usar fixtures pytest é outra solução acessível para ambas as classes de teste ou mesmo apenas funções de teste simples. Aqui está um exemplo de teste de funções 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..."

Portanto, a execução test_1...produz:

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

Observe que stuff_i_setupé referenciado no fixture, permitindo que esse objeto seja setupe torn downpara o teste com o qual está interagindo. Você pode imaginar que isso pode ser útil para um objeto persistente, como um banco de dados hipotético ou alguma conexão, que deve ser limpo antes de cada teste ser executado para mantê-los isolados.


13

Seu código deve funcionar exatamente como você espera se adicionar @classmethoddecoradores.

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

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

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


Isso é exatamente o que aparece na documentação. O problema que tive com o doutor foi que tive dificuldade em entender o contexto: self é tradicionalmente referido como self, não cls, então isso me pareceu estranho, fora do contexto da própria classe. Kiran (acima) fornece esse contexto.
Cognitiaclaeves

1
@Cognitiaclaeves "self é tradicionalmente referido como self, não cls" Sim, selfé usado para métodos de instância, onde o primeiro argumento é a instância do objeto específico no qual a operação do método está ocorrendo, enquanto clsé usado para @classmethods, que estão vinculados a a classe e não uma instância da classe (ou seja, um objeto).
code_dredd
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.