Fábricas de funções para dados aleatórios com py.test

O Py.test tem um excelente recurso chamado fixtures , que é uma forma pítônica de fazer injeção de dependência.

Exemplo rápido:

from random import randint
import pytest

@pytest.fixture
def number():
return randint(10)

@pytest.fixture
def structure(number):
return {'name': 'something',
'number': number}

def test_something(structure, number):
assert structure['number'] == number

O número aleatório será gerado apenas uma vez e, durante o teste, será sempre aquele número onde quer que seja injetado. Portanto, aqui o structuredispositivo vê o mesmo numberque test_something. Mas se o executarmos novamente, um novo número será gerado.

Isso é ótimo se você deseja gerar um único dado aleatório e verificá-lo mais tarde. Mas muitas vezes você realmente precisa de várias peças de dados aleatórios, e você não quer criar dispositivos elétricos como random_number1, random_number2etc.

Uma boa estratégia que descobri que funciona é, em vez de fazer com que o aparelho retorne os dados diretamente, em vez disso, o aparelho retorna uma função que tem acesso a outros aparelhos e que mantém o controle do que é gerado.

Isso se parece com o seguinte exemplo:

@pytest.fixture
def number():
return randint(10)

@pytest.fixture
def structure(number):
def _structure(name):
'''This function gets injected'''
retval
= {
'name': name,
'number': randint(number, 20)
}
# register the result in the function
_structure
.results[name] = retval
return retval
# before injecting, we create the registry
# on the function
_structure
.results = {}
return _structure

@pytest.fixture
def big_structure(structure):
return {
'foo': structure('bar')
}

Os pontos principais são:

  1. Faça com que o aparelho retorne uma função, em vez dos dados
  2. A função obtém acesso a quaisquer outros acessórios que você deseja (aqui, usamos o numberacessório como um piso para um novo número aleatório.
  3. Adicione um atributo de registro à função, para controlar os resultados
def test_something(structure, number, big_structure):
result
= structure('some_name')
assert result['number'] >= number
assert structure.results['some_name'] == result
assert structure.results['bar'] == big_structure['foo']

Por que a _structurefunção registra seus próprios resultados? Porque você pode precisar usar o structureacessório em outros acessórios. Aqui big_structureusa structuree se quisermos saber o que foi gerado, basta olhar no registro.

Além disso, como a função é criada por teste, o registro é apagado automaticamente para cada teste.