Mock vs MagicMock

138

Meu entendimento é que o MagicMock é um superconjunto do Mock que executa automaticamente "métodos mágicos", oferecendo suporte contínuo a listas, iterações e assim por diante ... Então, qual é o motivo da existência do Mock comum ? Não é apenas uma versão simplificada do MagicMock que pode ser praticamente ignorada? A classe Mock conhece algum truque que não está disponível no MagicMock ?

Vladimir Ignatov
fonte

Respostas:

99

Qual é o motivo da existência de Mock comum ?

O autor de Mock, Michael Foord, abordou uma questão muito semelhante no Pycon 2011 (31:00) :

P: Por que o MagicMock criou uma coisa separada, em vez de apenas dobrar a capacidade no objeto de simulação padrão?

R: Uma resposta razoável é que a maneira como o MagicMock funciona é que ele pré-configura todos esses métodos de protocolo criando novos Mocks e definindo-os, portanto, se cada novo mock criar um monte de novos mocks e defini-los como métodos de protocolo e, em seguida, todos esses protocolos métodos criaram muito mais zombarias e definiram-nos em seus métodos de protocolo, você tem recursão infinita ...

E se você quiser acessar o seu mock como um objeto de contêiner como um erro - não quer que isso funcione? Se todo mock tem automaticamente todos os métodos de protocolo, fica muito mais difícil fazer isso. Além disso, o MagicMock faz algumas dessas pré-configurações para você, definindo valores de retorno que podem não ser apropriados, então pensei que seria melhor ter essa conveniência que tenha tudo pré-configurado e disponível para você, mas você também pode fazer uma simulação comum objeto e apenas configure os métodos mágicos que você deseja que exista ...

A resposta simples é: basta usar o MagicMock em qualquer lugar, se esse for o comportamento que você deseja.

Ryne Everett
fonte
12
Eu acho que uma resposta melhor é: Use o MagicMock se você souber o que está fazendo, caso contrário, use o Mock.
Laike9m 6/05
56

Com o Mock, você pode zombar de métodos mágicos, mas precisa defini-los. O MagicMock possui "implementações padrão da maioria dos métodos mágicos". .

Se você não precisa testar nenhum método mágico, o Mock é adequado e não traz muitas coisas estranhas para os testes. Se você precisar testar muitos métodos mágicos, o MagicMock economizará algum tempo.

Sean Redmond
fonte
Com certeza eu já li a documentação. Isso não responde à minha pergunta - por que se preocupar com o Mock comum se o MagicMock faz exatamente o mesmo e muito mais? Não vejo coisas estranhas nos meus testes - basta usar o nome diferente e pronto. Então, onde está o problema?
precisa
39
Os testes devem ser mínimos e os objetos simulados devem ser minimamente funcionais, para que você tenha certeza exatamente do que está testando. Se você usa o MagicMock apenas porque faz mais, mas não está testando explicitamente todo esse "mais", corre o risco de falha no teste devido a um comportamento padrão do MagicMock. Essa falha pode refletir algo sobre os padrões do MagicMock mais do que aquilo que ele deveria zombar. Pior ainda, você corre o risco de um teste ser bem- sucedido quando deveria ter falhado. O risco é pequeno, mas se isso acontecer, você perderá muito tempo.
19613 Sean Redmond
1
Penso nisso como usar JS simples vs Jquery. Claro, você pode usar o Jquery para fazer todo o seu JS, mas em alguns casos, você só quer usar a ferramenta mínima necessária para realizar o trabalho. Acho que esses casos geralmente são extremamente simples ou extremamente complexos.
snuggles 25/03
49

Para começar, MagicMocké uma subclasse de Mock.

class MagicMock(MagicMixin, Mock)

Como resultado, o MagicMock fornece tudo o que o Mock fornece e muito mais. Em vez de pensar no Mock como uma versão simplificada do MagicMock, pense no MagicMock como uma versão estendida do Mock. Isso deve responder às suas perguntas sobre por que o Mock existe e o que o Mock fornece sobre o MagicMock.

Em segundo lugar, o MagicMock fornece implementações padrão de muitos / a maioria dos métodos mágicos, enquanto o Mock não. Veja aqui para mais informações sobre os métodos mágicos fornecidos.

Alguns exemplos de métodos mágicos fornecidos:

>>> int(Mock())
TypeError: int() argument must be a string or a number, not 'Mock'
>>> int(MagicMock())
1
>>> len(Mock())
TypeError: object of type 'Mock' has no len()
>>> len(MagicMock())
0

E estes que podem não ser tão intuitivos (pelo menos não intuitivos para mim):

>>> with MagicMock():
...     print 'hello world'
...
hello world
>>> MagicMock()[1]
<MagicMock name='mock.__getitem__()' id='4385349968'>

Você pode "ver" os métodos adicionados ao MagicMock quando esses métodos são chamados pela primeira vez:

>>> magic1 = MagicMock()
>>> dir(magic1)
['assert_any_call', 'assert_called_once_with', ...]
>>> int(magic1)
1
>>> dir(magic1)
['__int__', 'assert_any_call', 'assert_called_once_with', ...]
>>> len(magic1)
0
>>> dir(magic1)
['__int__', '__len__', 'assert_any_call', 'assert_called_once_with', ...]

Então, por que não usar o MagicMock o tempo todo?

A pergunta de volta para você é: Você concorda com as implementações padrão do método mágico? Por exemplo, não há problema em mocked_object[1]não errar? Você concorda com quaisquer conseqüências não intencionais devido às implementações do método mágico já estarem lá?

Se a resposta a essas perguntas for afirmativa, vá em frente e use o MagicMock. Caso contrário, atenha-se ao mock.

user650654
fonte
12

Isto é o que a documentação oficial do python diz:

Na maioria desses exemplos, as classes Mock e MagicMock são intercambiáveis. Como o MagicMock é a classe mais capaz, é sensato usar por padrão.

user2916464
fonte
3

Eu encontrei outro caso específico em que simples Mock pode se tornar mais útil do que MagicMock:

In [1]: from unittest.mock import Mock, MagicMock, ANY
In [2]: mock = Mock()
In [3]: magic = MagicMock()
In [4]: mock.foo == ANY
Out[4]: True
In [5]: magic.foo == ANY
Out[5]: False

Comparar com ANYpode ser útil, por exemplo, comparar quase todas as chaves entre dois dicionários em que algum valor é calculado usando uma simulação.

Isso será válido se você estiver usando Mock:


self.assertDictEqual(my_dict, {
  'hello': 'world',
  'another': ANY
})

enquanto aumentará um AssertionErrorse você usouMagicMock

Manu
fonte