Pergunta

Eu estou em um projeto onde nós estamos começando refatoração algum código base maciça. Um problema que imediatamente surgiu é que cada importação de arquivo um monte de outros arquivos. Como faço de uma forma elegante zombar isso no meu teste de unidade sem ter que alterar o código real para que eu possa começar a escrever unidade-testes?

Como um exemplo: O arquivo com as funções que eu quero testar, as importações dez outros arquivos que faz parte do nosso software e não libs núcleo python

.

Eu quero ser capaz de executar os testes de unidade como separadamente quanto possível e por enquanto eu só estou indo para funções de teste que não dependem de coisas dos arquivos que estão sendo importados.

Obrigado por todas as respostas.

Eu realmente não sabia o que eu queria fazer desde o início, mas agora eu acho que sei.

O problema era que algumas importações só foi possível quando toda a aplicação estava funcionando por causa de algum de terceiros auto-magic. Então eu tive que fazer alguns tocos para estes módulos em um diretório que eu apontadas com sys.path

Agora eu posso importar o arquivo que contém as funções Quero testes de gravação para no meu arquivo-teste de unidade, sem queixas sobre módulos em falta.

Foi útil?

Solução

Se você quiser importar um módulo e, ao mesmo tempo garantir que ele não faz nada de importação, você pode substituir o __import__ builtin função.

Por exemplo, usar essa classe:

class ImportWrapper(object):
    def __init__(self, real_import):
        self.real_import = real_import

    def wrapper(self, wantedModules):
        def inner(moduleName, *args, **kwargs):
            if moduleName in wantedModules:
                print "IMPORTING MODULE", moduleName
                self.real_import(*args, **kwargs)
            else:
                print "NOT IMPORTING MODULE", moduleName
        return inner

    def mock_import(self, moduleName, wantedModules):
        __builtins__.__import__ = self.wrapper(wantedModules)
        try:
            __import__(moduleName, globals(), locals(), [], -1)
        finally:
            __builtins__.__import__ = self.real_import

E em seu código de teste, em vez de escrever import myModule, escreve:

wrapper = ImportWrapper(__import__)
wrapper.mock_import('myModule', [])

O segundo argumento para mock_import é uma lista de nomes de módulos que você do deseja importar no módulo interno.

Este exemplo pode ser modificado adicionalmente para, por exemplo, importar outro módulo do que o desejado, em vez de simplesmente não importá-lo, ou mesmo zombar o objeto módulo com algum objeto personalizado de sua preferência.

Outras dicas

Se você realmente quer mexer com o mecanismo de importação Python, dê uma olhada na ihooks módulo . Ele fornece ferramentas para alterar o comportamento do __import__ embutido. Mas não é claro da sua pergunta por que você precisa para fazer isso.

"importações um monte de outros arquivos"? Importa um monte de outros arquivos que fazem parte de sua base de código personalizado? Ou importações um monte de outros arquivos que fazem parte da distribuição Python? Ou importação um monte de outros arquivos do projeto de código aberto?

Se suas importações não fazer o trabalho, você tem um problema "simples" PYTHONPATH. Obter todos os seus vários diretórios do projeto em um PYTHONPATH que você pode usar para testar. Temos um caminho bastante complexo, no Windows que gerenciá-lo como este

@set Part1=c:\blah\blah\blah
@set Part2=c:\some\other\path
@set that=g:\shared\stuff
set PYTHONPATH=%part1%;%part2%;%that%

Nós manter cada pedaço do caminho separado para que (a) sabe onde as coisas vêm e (b) pode gerir a mudança quando mover coisas ao redor.

Uma vez que o PYTHONPATH é pesquisada em ordem, podemos controlar o que é usado ajustando a ordem no caminho.

Uma vez que você tem "tudo", torna-se uma questão de confiança.

De qualquer

  • você confiar em algo (ou seja, a base de código Python) e apenas importá-lo.

ou

  • Você não fazer algo confiança (ou seja, o seu próprio código) e você

    1. testá-lo separadamente e
    2. zombar-lo para o teste stand-alone.

Você testar as bibliotecas Python? Se assim for, você tem um monte de trabalho. Se não, então, você deve talvez apenas zombar fora as coisas que você está realmente indo para teste.

Sem manipulação difícil é necessário se você quer uma solução rápida e suja antes de suas unidades-testes.

Se os testes de unidade estão no mesmo arquivo como o código que você deseja testar, basta apagar módulo indesejado do dicionário globals().

Aqui está um exemplo bastante longa: suponha que você tenha um impp.py módulo com conteúdo:

value = 5

Agora, em seu arquivo de teste, você pode escrever:

>>> import impp
>>> print globals().keys()
>>> def printVal():
>>>     print impp.value
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__']

Note que impp está entre os globals, porque foi importado. Chamar a função printVal esse módulo usos impp ainda funciona:

>>> printVal()
5

Mas agora, se você retirar a chave impp de globals() ...

>>> del globals()['impp']
>>> print globals().keys()
['printVal', '__builtins__', '__file__', '__name__', '__doc__']

... e tentar chamar printVal(), você vai ter:

>>> printVal()
Traceback (most recent call last):
  File "test_imp.py", line 13, in <module>
    printVal()
  File "test_imp.py", line 5, in printVal
    print impp.value
NameError: global name 'impp' is not defined

... que é provavelmente exatamente o que você está tentando alcançar.

Para usá-lo em sua unidade-testes, você pode excluir os globals logo antes de executar o conjunto de testes, por exemplo, em __main__:

if __name__ == '__main__':
    del globals()['impp']
    unittest.main()

Em seu comentário acima , você diz que quer convencer python que certos módulos já foram importados. Isso ainda parece um objetivo estranho, mas se isso é realmente o que você quer fazer, em princípio, você pode esgueirar atrás das costas do mecanismo de importação, e mudança sys.modules. Não sei como this'd trabalho para as importações de pacote, mas deve ser bom para as importações absolutos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top