Похоже, что макет патча Python вызывается, но утверждение не выполняется

Я использую Python 2.6.6

То, что я пытаюсь сделать, это заменить создание объекта Mock, чтобы убедиться, что сделаны правильные вызовы. Должно быть прямо.

Мой модуль:

import dir.SubModule

class Cls( object ):
    def Start( self ):
        self.__obj = dir.SubModule.SubCls()
        self.__obj.foo()

Мой тест:

import MyModule
import unittest
from mock import Mock, MagicMock, patch

class MyTest( unittest.TestCase ):
    def setUp( self ):
        self.uut = MyModule.Cls()

    def test_one( self ):
        with patch( 'dir.SubModule.SubCls', spec=True ) as mockObj:
            print "mock calls before"
            print mockObj.mock_calls
            self.uut.Start()

            print "called: "      + str( mockObj.called )
            print "foo called: " + str( mockObj.foo.called )
            print "call_count: "  + str( mockObj.call_count )
            print "call_args: "   + str( mockObj.call_args )
            print "args_list: "   + str( mockObj.call_args_list )
            print "mock calls:\n" + str( mockObj.mock_calls )
            print "method calls:\n " + str( mockObj.method_calls )

Результат:

mock calls before:
[]
called: True
foo called: False
call_count: 1
call_args: call()
args_list: [call()]
mock calls:
[call(), call().foo()]
method calls:
[]

И все же тест не проходит:

AssertionError: Expected call: foo()
Not called

Я не понимаю, как макет может сообщить, что звонки были сделаны, но я не могу утверждать, что они были вызваны. Что мне не хватает?

РЕДАКТИРОВАТЬ: после добавления в отчеты обо всех метриках кажется, что есть что-то фундаментальное, что я неправильно понимаю в отношении макетов python. Если foo() находится в списке вызовов, то почему количество вызовов равно 1 и почему foo.call сообщает «False»?


person PfunnyGuy    schedule 17.03.2016    source источник
comment
Код, который вы разместили, кажется, не соответствует коду, который вы на самом деле выполнили достаточно хорошо, чтобы мы могли вам помочь. Похоже, что код, который вы опубликовали, содержит совершенно другой набор ошибок.   -  person user2357112 supports Monica    schedule 17.03.2016
comment
Единственная ошибка, которую я обнаружил, заключалась в том, что мне не хватало объявления setUp() перед созданием моего объекта uut; в противном случае это код, который я запускаю, за исключением того, что я удалил другие ненужные объекты и создание и использование переменных (что я доказал, потому что это закомментировано в моем коде).   -  person PfunnyGuy    schedule 17.03.2016
comment
Я бы очень хотел, чтобы вы написали что-нибудь более практичное. Я изо всех сил пытаюсь понять foos и неправильный корпус Python.   -  person Bobort    schedule 07.07.2020


Ответы (3)


mockObj.foo никогда не вызывается в этом тесте. self.uut.Start() вызывает mockObj, создавая новый макет, а затем вызывает метод этого макета foo. Если вы хотите утверждать, что этот вызов произошел, вам нужно будет получить доступ к правильному объекту:

mockObj.return_value.foo.assert_called_with()
person user2357112 supports Monica    schedule 17.03.2016
comment
Ах! Я читал о чем-то подобном в другой статье. Мне нужно найти это и связать, у него было довольно хорошее объяснение! Спасибо! - person PfunnyGuy; 17.03.2016

Вот ссылка: ссылка

Мое простое решение:

with patch( 'class' ) as mockCreator:
    mockObj = mockCreator.return_value

Тогда я могу волей-неволей использовать mockObj.

Я наткнулся на связанное решение на прошлой неделе, когда впервые узнал о моках — слишком рано, чтобы информация полностью усвоилась.

person PfunnyGuy    schedule 17.03.2016

Синтаксис фиктивного утверждения имеет много особенностей. Чтобы справиться с этим, я написал вспомогательную библиотеку для создания утверждений для меня.

Вот как вы могли бы использовать его для своего метода тестирования:

import MyModule
import unittest
from mock import patch

class MyTest( unittest.TestCase ):
    def setUp( self ):
        self.uut = MyModule.Cls()

    def test_one( self ):
        with patch( 'dir.SubModule.SubCls', spec=True ) as mockObj:
            self.uut.Start()

            # calls to generate_asserts, put this after the 'act'
            import mock_autogen.generator
            print(mock_autogen.generator.generate_asserts(mockObj))

Результат, который вы получаете:

assert 1 == mockObj.call_count
mockObj.assert_called_once_with()
mockObj.return_value.foo.assert_called_once_with()

Что соответствует вызову __init__, за которым следует вызов foo вновь созданного объекта.

Так что больше не нужно выяснять точный синтаксис самостоятельно, просто примените инструмент :)

person Peter K    schedule 06.09.2019