Python,-teste de unidade e importações zombando
-
05-07-2019 - |
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.
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" PYTHONPAT
H. 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ê
- testá-lo separadamente e
- 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.