Afirmar que uma função / método não foi chamado usando o Mock


131

Estou usando a biblioteca Mock para testar meu aplicativo, mas quero afirmar que alguma função não foi chamada. Os documentos falsos falam sobre métodos como mock.assert_called_withe mock.assert_called_once_with, mas não encontrei nada parecido mock.assert_not_calledou algo relacionado para verificar se o mock NÃO foi chamado .

Eu poderia usar algo como o seguinte, embora não pareça legal nem pitônico:

def test_something:
    # some actions
    with patch('something') as my_var:
        try:
            # args are not important. func should never be called in this test
            my_var.assert_called_with(some, args)
        except AssertionError:
            pass  # this error being raised means it's ok
    # other stuff

Alguma idéia de como fazer isso?


Como o @Ahmet aponta em sua resposta, assert_not_called agora é suportado, também no backport ( docs.python.org/3/library/… ).
Martin

Respostas:


144

Isso deve funcionar para o seu caso;

assert not my_var.called, 'method should not have been called'

Amostra;

>>> mock=Mock()
>>> mock.a()
<Mock name='mock.a()' id='4349129872'>
>>> assert not mock.b.called, 'b was called and should not have been'
>>> assert not mock.a.called, 'a was called and should not have been'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: a was called and should not have been

Esta resposta requer Django? Estou recebendo um erro:AttributeError: MockCallable instance has no attribute 'called'
Nathan Arthur

@ NathanArthur Hm, acho que não, depois sudo easy_install -U mocke from mock import Mockno MacOS, o acima é executado sem problemas. Nunca instalado Django :)
Joachim Isaksson

Hmm. Isso é estranho. Estou executando o Python 2.7.1 e estou usando o unittest e from mock import Mockcom o Python Mock 0.1.0 para meus testes. Isso soa problemático?
Nathan Arthur

Estou zombando de uma classe que pode ser chamada de outro módulo, assim parece module_to_test.another_module.class = mock.Mock(), você pode confirmar que isso não se lembra de chamadas em diferentes casos de teste (instâncias unittest.TestCase)? Eu acho que a contagem de chamadas não é redefinida neste caso
0xc0de 17/07/2015

66

Embora seja uma pergunta antiga, gostaria de adicionar que atualmente a mockbiblioteca (backport of unittest.mock) suporta o assert_not_calledmétodo.

Basta atualizar o seu;

pip install mock --upgrade


29

Você pode verificar o calledatributo, mas se sua afirmação falhar, a próxima coisa que você deseja saber é algo sobre a chamada inesperada; portanto, você também pode providenciar para que essas informações sejam exibidas desde o início. Usando unittest, você pode verificar o conteúdo call_args_list:

self.assertItemsEqual(my_var.call_args_list, [])

Quando falha, envia uma mensagem como esta:

AssertionError: as contagens de elementos não eram iguais:
O primeiro tem 0, o segundo tem 1: call ('primeiro argumento', 4)

14

Quando você testa usando a classe herda unittest.TestCase, você pode simplesmente usar métodos como:

  • assertTrue
  • assertFalse
  • assertEqual

e similar (na documentação do python, você encontra o resto).

No seu exemplo, podemos simplesmente afirmar se a propriedade mock_method.called é False , o que significa que o método não foi chamado.

import unittest
from unittest import mock

import my_module

class A(unittest.TestCase):
    def setUp(self):
        self.message = "Method should not be called. Called {times} times!"

    @mock.patch("my_module.method_to_mock")
    def test(self, mock_method):
        my_module.method_to_mock()

        self.assertFalse(mock_method.called,
                         self.message.format(times=mock_method.call_count))

10

Com python >= 3.5você pode usar mock_object.assert_not_called().


1

A julgar por outras respostas, ninguém, exceto @ rob-kennedy, falou sobre o call_args_list.

É uma ferramenta poderosa para a qual você pode implementar exatamente o contrário de MagicMock.assert_called_with()

call_args_listé uma lista de callobjetos. Cada callobjeto representa uma chamada feita em uma chamada simulada.

>>> from unittest.mock import MagicMock
>>> m = MagicMock()
>>> m.call_args_list
[]
>>> m(42)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42)]
>>> m(42, 30)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42), call(42, 30)]

Consumir um callobjeto é fácil, pois você pode compará-lo com uma tupla de comprimento 2, onde o primeiro componente é uma tupla contendo todos os argumentos posicionais da chamada relacionada, enquanto o segundo componente é um dicionário dos argumentos da palavra-chave.

>>> ((42,),) in m.call_args_list
True
>>> m(42, foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((42,), {'foo': 'bar'}) in m.call_args_list
True
>>> m(foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((), {'foo': 'bar'}) in m.call_args_list
True

Portanto, uma maneira de abordar o problema específico do OP é

def test_something():
    with patch('something') as my_var:
        assert ((some, args),) not in my_var.call_args_list

Observe que dessa maneira, em vez de apenas verificar se uma chamada simulada foi chamada, via MagicMock.called, agora você pode verificar se ela foi chamada com um conjunto específico de argumentos.

Isso é útil. Digamos que você queira testar uma função que recebe uma lista e chama outra função compute(), para cada valor da lista somente se eles satisfizerem uma condição específica.

Agora você pode zombar computee testar se foi chamado com algum valor, mas não com outros.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.