Como lidar adequadamente com parâmetros globais para testes de unidade em python?

11

Estamos implementando muitos algoritmos que normalmente têm muitos parâmetros compartilhados, conhecidos publicamente e relevantes para a segurança.

Atualmente, simplesmente usamos uma classe contendo todos os parâmetros e dois objetos globais predefinidos:

class PublicParams(object):
    p = q = 0

    def __init__(self, p, q):
        self.p = p
        self.q = q

# used for tests
publicParams_test = PublicParams(15,7)               

# Some 2048 bit numbers for example
publicParams_secure = PublicParams(128378947298374928374,128378947298374928374)  

Os algoritmos, então, tomam um PublicParamsobjeto como um argumento que assume como padrão opublicParams_secure

def AlgoOne(n, publicParams = publicParams_secure):
    # do stuff with publicParams.p
    # ...
    AlgoTwo(x, publicParams)

e

def AlgoTwo(x, publicParams= publicParams_secure):
    # do stuff with publicParams.q

Dessa forma, ainda podemos injetar diferentes parâmetros públicos para facilitar o teste de unidade:

class AlgoOneTest(unittest.TestCase):
    def test(self):
        # compare with manually computed result
        self.assertTrue(AlgoOne(1, publicParams_test) == 10) 

O que eu não gosto nessa abordagem:

  • A atribuição de publicParamsum valor padrão o torna opcional ao chamar algum algoritmo. No entanto, fica fácil esquecer de transmiti-lo ao chamar AlgoTwode dentro AlgoOne, o que resultaria em dois objetos diferentes sendo usados ​​se o objeto de teste fosse passado paraAlgoOne

Existe uma maneira melhor que seja menos propensa, mas ainda ofereça flexibilidade para testes de unidade? Esta é realmente a melhor prática?

netik
fonte

Respostas:

1

Crie arquivos de configuração test_config.pye production_config.py. Selecione um deles usando a variável de ambiente ou um argumento da linha de comandos. Importe-o (ou leia / analise, se você escolher .json/ em .txtvez de .py) e disponibilize o resultado para todo o programa por meio de um objeto global em um módulo que você pode importar em qualquer lugar.

Isso é muito parecido com o que você já está fazendo, exceto que leva um passo adiante, do escopo global para o shell a partir do qual você chama python. A vantagem é que não há mais risco de misturar acidentalmente a produção e a configuração de teste: você não pode ler os dois arquivos na mesma sessão python, pois existe apenas uma variável de ambiente / linha de comando.

max
fonte
0

Há várias coisas que você pode fazer.

  • Pare de usar globals
  • Parar de usar padrões
  • Sempre teste através de métodos auxiliares privados que não permitem o uso dos padrões

    def _AlgoOne(n, publicParams):
        return AlgoOne(n, publicParams)
    

Claro que qualquer uma dessas opções dá muito trabalho, mas você não perguntaria se isso não era um problema para você.

candied_orange
fonte
0

Sempre se pode separar a coleção de valores do contexto global e o processamento desses parâmetros.

def do_the_thing():
    """Provides the public (rather untestable) context.
    _do_the_thing(global1, global2, publicParams)"""

def _do_the_thing(blah, blah, blah):
    "Actually does the thing"
    pass
user166988
fonte