Python zombando há algo intuitivos sobre você. Você não é fácil de lidar e leva vários ” Ah, agora eu entendo!”momentos antes de você ser melhor compreendido., Você é uma ferramenta poderosa e fantástica para permitir testes de unidade automatizados, mas eu conheço muitos desenvolvedores que “sobrevivem” com o conhecimento que têm e escrever esses testes leva mais tempo do que deveria.
existem muitos artigos sobre troça, e sim, estou agora escrevendo outro. No entanto, eu realmente quero tentar explicar claramente, especialmente aqueles ” Aha!”momentos que fizeram avançar a minha compreensão.em primeiro lugar, o que é zombar? Aqui está uma citação dos docs:
mock é uma biblioteca para testes em Python., Ele permite que você substitua partes do seu sistema sob teste por objetos mock e fazer afirmações sobre como eles foram usados.
portanto “permite-lhe substituir partes dos seus sistemas”, mas que partes? Acontece que você pode zombar de praticamente qualquer coisa que você pode definir, como:
- funções
- classes
- objetos
Você substituí-los com “mock objects”, que são instâncias da Simulação ou MagicMock classe.,
Você então “fazer afirmações” sobre essa instância Mock, que é uma maneira de verificar se a instância Mock foi usada da maneira que você estava esperando.
neste artigo eu vou cobrir o básico de substituir partes de seus sistemas, mas eu não vou estar cobrindo fazer afirmações.
vamos começar com um exemplo simples. Eu vou criar um novo módulo chamado simple.py e no módulo que eu estou indo para definir uma função chamada simple_function que apenas retorna uma seqüência de caracteres, como por exemplo:
def simple_function():
return "You have called simple_function"
Agora, eu estou indo para criar um segundo módulo chamado use_simple.py., Neste módulo vou importar o pacote de simulação de unittest e importar o módulo simples:
from unittest import mockimport simple
Seguinte, estou indo para uma gravação de duas funções, uma que chama simple_function normalmente e que zomba a chamada para ele. Note que normalmente você faria isso em um contexto de teste, mas eu estou removendo isso propositadamente para que eu possa me concentrar na parte zombando. A primeira função:
def use_simple_function():
result = simple.simple_function()
print(result)use_simple_function()
como você pode ver, apenas imprime o resultado de uma simples função., Especificamente, a saída é:
You have called simple_function
para a segunda função, para gozar a função simple_ vou usar a simulação.decorador de adesivos. Este decorador permite-lhe especificar o que deseja zombar, passando-lhe uma string no Pacote format’.modulo.FunctionName”., No nosso exemplo, não há nenhum pacote, mas o módulo é chamado de simples e a função é chamada simple_function, de modo que o decorador terá a seguinte aparência:
@mock.patch('simple.simple_function')
, temos que escrever a nossa segunda função assim:
@mock.patch('simple.simple_function')
def mock_simple_function():
eu tê-la chamado mock_simple_function, mas ele pode ser chamado de qualquer coisa. Estamos perdendo um parâmetro aqui porque quando você decora uma função com @mock.patch it will pass an instance of the MagicMock class (a MagicMock object) that is used to replace the function you are mocking., Então ele se parecerá com isto:
@mock.patch('simple.simple_function')
def mock_simple_function(mock_simple_func):
assim, especificando @mock.patch (‘simples.simple_function’) estou a dizer que quero substituir o simple_function pelo objecto MagicMock atribuído ao parâmetro mock_simple_func.
Inicialmente, vamos aprofundar a função de impressão mock_simple_func e, em seguida, chamá-lo de:
Quando executado, a saída é:
<MagicMock name='simple_function'>
o Que é um MagicMock objeto que será chamado em vez de simple_function., É importante notar que o atributo nome do objeto é a coisa que está sendo ridicularizada, e o id é um número único para o objeto.
Se agora, também, imprimir simple_function (mas não chamá-lo):
Que produz a saída:
<MagicMock name='simple_function'>
<MagicMock name='simple_function'>
Você pode ver que a partir da correspondência de identificações que mock_simple_func é o mesmo MagicMock objeto como simple_function.até agora tudo bem. Gozámos com uma função simples. Para ser explícito, simple_function foi ridicularizado porque foi substituído por um objeto MagicMock.,
Vamos adicionar as linhas a partir da primeira função:
Quando executado, a saída é:
Como você pode ver a partir da terceira linha de saída quando simple_function na verdade, é chamado ele cria um diferente MagicMock objeto. Lembre-se que, uma vez que temos zombado de simples função, é na verdade um objeto MagicMock, então quando chamamos simple_ função estamos realmente chamando o objeto MagicMock!para mim, é aqui que se instala a confusão.
- Por que é criado um novo objecto MagicMock?e como é que se pode chamar um objecto?, Normalmente obtém-se” TypeError: ‘* * * ‘o objecto não pode ser ligado” quando se tenta.
vamos parar por um minuto para olhar mais de perto para MagicMock.
MagicMock tem magia no seu nome porque tem implementações padrão da maioria dos métodos de magia python. O que é um método de magia python que você pergunta? É todas as funções especiais python que têm duplo sublinhado no início e no fim do seu nome. Você pode encontrar uma lista deles aqui. O que interessa aqui é ligar., A chamada de função é descrita como:
Chamado quando um objeto é chamada de uma função
Então, se uma classe implementa esta função, você pode criar uma instância da classe e, em seguida, chamada de instância como uma função i.e. :
Portanto, quando MagicMock como é chamada uma função, o __chamada__ função é chamada. A implementação desta função por MagicMock cria um novo mock!
assim, quando chamamos simple_function () um novo mock é criado e devolvido. Não só isso, mas também temos o conceito de gozação entre pais e filhos., Quando um trocadilho cria outro, torna-se o pai e o recém-criado, o filho. Essa brincadeira de criança pode criar outras escárnio para que você acabe com uma herarquia de objetos escarnecidos.
Learning point 1: When a MagicMock object is called like a function, by default it creates and returns a new MagicMock object.de volta ao nosso exemplo. Até agora temos apenas zombado simple_function, mas não fez nada com ele, a não ser imprimir os MagicMocks que são criados como resultado. Digamos que eu queria mudar o que foi devolvido de simple_function(), como eu faria isso?, Eu gostaria de fazer isso usando o return_value propriedade de MagicMock assim (eu também estou indo para remover as duas primeiras impressões como eles não são mais necessários):
Quando executado, a saída é:
You have mocked simple_function
Como você pode ver quando simple_function() é agora chamado de MagicMock return_value é retornado em vez de criar uma segunda MagicMock objeto.
Ponto de Aprendizagem 2: para mudar o que é devolvido quando um objeto MagicMock é chamado como uma função, set return_value.
Se você quiser fazer mais do que alterar o valor de retorno, então você pode usar o MagicMock.funcionalidade de efeito secundário., Ele permite que você especifique uma função inteiramente nova que será chamada em vez da que você está zombando. Vamos a isto. Em primeiro lugar, vou definir uma nova função que vai ser o side_effect (nota: ele precisa ter o mesmo parâmetro definido como a função que você está zombando):
def side_effect_function():
return "SKABLAM!"
Agora podemos definir uma nova função que usa esse efeito colateral assim:
Tudo é o mesmo como o mock_simple_function mais acima, exceto que eu estou ajustando o side_effect de mock_simple_func em vez de return_value.,
a saída é:
um bom caso de utilização para o uso do efeito secundário é se você quiser testar um fluxo de erro e, portanto, você quer criar uma exceção no seu teste. Eu vou mudar o side_effect_function para gerar um erro em vez disso:
def side_effect_function():
raise FloatingPointError("A disastrous floating point error has occurred")
Agora, a saída é (eu removi a parte de rastreio para maior clareza):
Traceback (most recent call last):
File "use_simple.py”, line 18, in side_effect_function
raise FloatingPointError("A disastrous floating point error has occurred”)
FloatingPointError: A disastrous floating point error has occurred
de Aprendizagem ponto 3: Para fazer mais do que apenas mudar o return_value de um MagicMock, defina side_effect.
classes de escárnio
até agora nós apenas zombamos de uma função, mas que tal uma classe?, Vamos definir uma classe no simple.py chamado SimpleClass com um método chamado explodir:
class SimpleClass(object):
def explode(self):
return "KABOOM!"
Agora no use_simple.py vamos escrever uma função que utiliza SimpleClass e, em seguida, chamar essa função:
def use_simple_class():
inst = simple.SimpleClass()
print(inst.explode())use_simple_class()
Quando executar a saída é:
KABOOM!
Agora, vamos definir uma segunda função e escolha a zombar da totalidade de simple_class usando o @simulação.decorador de patches:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
como quando zombando de uma função, o @mock.,o decorador de patch passa por um objecto MagicMock que substitui a classe que está a gozar na função que está a decorar. O objeto MagicMock neste caso é atribuído ao argumento mock_class.
Para o momento em que eu simplesmente imprima o MagicMock objeto e, em seguida, executar a função:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)mock_simple_class()
Quando executar a saída é:
<MagicMock name='SimpleClass'>
Como com o escárnio de uma função de uma MagicMock objeto é criado e transmitido., Agora vamos imprimir SimpleClass, então você pode ver ele foi escarnecido e substituídos com o mesmo MagicMock objeto:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)
print(simple.SimpleClass)
A saída agora é:
<MagicMock name='SimpleClass'>
<MagicMock name='SimpleClass'>
Vamos criar uma instância do SimpleClass i.e. chamada SimpleClass () e, em seguida, imprima-o. Lembre-se, uma vez que temos ridicularizado SimpleClass, o que de fato vai acontecer é que nós será chamado a MagicMock objeto, como uma função:
O resultado é:
Então, como esperado, chamando SimpleClass() e, portanto, chamar a MagicMock objeto como uma função cria um novo MagicMock objeto.,
até agora, zombar de uma classe tem sido muito parecido com zombar de uma função. No entanto, na realidade, quando definimos uma classe, então vamos criar objetos usando essa classe e são esses objetos mais frequentemente do que não que nós realmente queremos zombar. A questão é, como você zombar de uma instância criada a partir de uma classe? Bem, quando você zombar de uma classe e, portanto, uma MagicMock objeto é criado, se você se referir a return_value em que MagicMock objeto cria um novo MagicMock objeto que representa a instância da classe criada. Caramba! Foi uma frase e meia! Este foi definitivamente um dos meus ” Aha!,”momentos, mas mesmo escrevê-lo não deixa claro. Vamos imprimir return_value para que você possa ver o que eu quero dizer:
O resultado é:
A terceira linha é a impressão de que a instância da classe criada, e a quarta linha é a impressão de que o return_value do MagicMock objeto passado para a função. Como podem ver, estão a referir-se ao mesmo objecto MagicMock.
Ponto de Aprendizagem 4: quando se goza de uma classe, é criado um objecto MagicMock. Quando você cria uma instância dessa classe um novo objeto MagicMock é criado., Quando você se refere ao valor de retorno do objeto MagicMock dessa classe você recebe o mesmo objeto MagicMock que foi criado quando a instância dessa classe foi criada.Ahhhhh, porque é que isso é importante?!?!?”Ouço-te chorar!
“é importante se você quiser zombar da função explode” I bellow in return!
a próxima pergunta que você precisa fazer é: é o método que eu quero ridicularizar um método de classe ou um método de instância? Porque como você muda o valor de retorno do método será diferente dependendo da resposta.,
Se o método é um método de classe, em seguida, você deve definir o return_value assim:
MagicMockObject.ClassMethodName.return_value = "A delightful return value"
Mas se é um método de instância, você faria:
MagicMockObject.return_value.InstanceMethodName.return_value = "A daring return value"
Com isso em mente, vamos mudar o return_value de explodir, o que é um método de instância (eu vou remover a maioria das instruções de impressão para maior clareza) :
No código acima, mock_class.return_value retorna o objeto MagicMock que representa a instância de SimpleClass.
mock_ class.return_value.,explode retorna o objeto MagicMock que representa o método de explosão da instância de SimpleClass.
portanto, definir o valor de retorno de mock_class.return_value.explode define o valor de retorno do método explode da instância de SimpleClass.
a saída é:
BOO!
muito semelhante ao zombar de uma função, você pode definir o efeito secundário para métodos de classes e objetos também.espero que este artigo o tenha ajudado a compreender um pouco mais a troça. Por favor, recomendo se você gostou e sinta-se livre para deixar uma resposta como eu gostaria de ouvir de você.