Estou muito confuso e um pouco preocupado ao descobrir o seguinte comportamento, onde tenho dois testes e dois acessórios.
import pytest
@pytest.fixture
def new_object():
return list()
@pytest.fixture
def a_string(new_object):
# Change this instance of the object
new_object.append(1)
return "a string"
def test_1(new_object):
assert len(new_object) == 0
def test_2(a_string, new_object):
assert len(new_object) == 0
O primeiro teste passa, mas o segundo falha.
def test_2(a_string, new_object):
> assert len(new_object) == 0
E assert 1 == 0
E + where 1 = len([1])
tests/test_pytest_list.py:21: AssertionError
================================================ short test summary info =================================================
FAILED tests/test_pytest_list.py::test_2 - assert 1 == 0
============================================== 1 failed, 1 passed in 0.36s ===============================================
Eu esperava que os equipamentos passassem novas instâncias de um objeto (a menos que especificado de outra forma), não o mesmo objeto que algum outro equipamento modificou.
De acordo com a documentação sobre o escopo dos acessórios, está escrito:
o padrão é invocar uma vez por função de teste
Outro equipamento não se qualifica como uma função?
ATUALIZAR
Com base nos comentários, agora entendo o problema, embora eu ainda ache que é um comportamento perigoso para uma ferramenta de teste de unidade.
Aqui está outro uso inválido de fixtures que uma pessoa ingênua como eu pode não perceber que é errado:
@pytest.fixture
def new_object():
"""I want to test instances of this class"""
return list()
@pytest.fixture
def case_1(new_object):
new_object.append(1)
return new_object
@pytest.fixture
def case_2(new_object):
new_object.append(2)
return new_object
def test_cases(case_1, case_2):
assert sum(case_1) + sum(case_2) == 3 # fails: assert (3 + 3) == 3
O acessório
new_object
é invocado para cada teste, conforme você declarou claramente na documentação.O problema está no seu segundo equipamento e no uso da combinação de ambos no seu segundo teste.
Como o pytest permite que você use fixtures mais de uma vez por teste sem afetar uns aos outros, usando retornos em cache .
Isso significa que, quando o equipamento
a_word
usa o equipamentonew_object
pela segunda vez,new_object
ele é encontrado no seu segundo teste e usa o valor de retorno armazenado em cache daa_word
chamada do equipamento em vez de fornecer outra lista vazia.O erro que cometi foi considerar um fixture como um método de fábrica que sempre constrói uma nova instância. Uma maneira melhor de pensar nisso talvez seja um dicionário com um valor padrão que cria apenas uma instância para cada teste e, se chamado mais de uma vez, retorna a mesma instância. O objetivo é evitar recalcular o fixture mais de uma vez (como explicado na resposta de @Wolric).
Então, para o que quero fazer, uma solução simples é criar uma função de fábrica real fora do Pytest:
Alternativamente, você pode retornar o método de fábrica como um acessório: