Python unittest patch, использующий пользовательский класс mock - PullRequest
0 голосов
/ 25 марта 2019

Я тестирую относительно простой кусок кода, который использует два относительно сложных объекта, требующих доступа к диску.Сложные объекты из библиотеки.Я хотел бы посмеяться над этими объектами библиотеки и утверждать, что некоторые из их функций-членов были вызваны с конкретными значениями, в зависимости от ввода.

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

Кажется, что вы должны иметь возможность предоставить собственный класс и обернуть его объектом Mock для отслеживания всех вызовов функций-членов.Я потратил несколько часов, пытаясь разобраться в официальной документации , и самый близкий мне аргумент wraps.К сожалению, кажется, что это не заменяет все члены на mock, которые обертывают, вместо этого, он просто использует переменные-члены mock объекта, отслеживая только вызовы к __init__.

, например, в module_a.py:

# Assuming some library class that acts something like this:
class A:
  def __init__(self, filename):
    self.filename = filename
  def get(self):
    # ... Some complicated code, involving state and disk access ...
    return something

def some_fnc(filename):
  a = A(filename)
  a.get()
  a.get()
  a.get()
  a.get()

in test_module_a.py:

import unittest
import unittest.mock

import module_a

class MockA:
  def __init__(self, *args, **kwargs):
    self.i = 0
    pass
  def get(self):
    # Fake version that returns dummy values,
    # but more complicated than simply returning some value.
    # Easy to reason about. If this gets complicated then you have problems.
    print(self.i, 'is gotten')
    if self.i > 3:
      return 5
    self.i += 1
    return 1

class SomeFncTestCase(unittest.TestCase):
  @unittest.mock.patch('module_a.A', wraps=MockA)
  def test_it(self, m):
    module_a.some_fnc('in_file')
    m.assert_called_with('in_file')
    m.get.assert_called()

MockA.get вызывается, и первое утверждение проходит, ноВторой assert терпит неудачу.

Есть некоторые функции, которые я пропускаю?Можно ли делать то, что я хочу?Есть ли причина, почему то, что я предлагаю, особенно плохо?

1 Ответ

0 голосов
/ 26 марта 2019

Вы можете использовать объект patch ed Mock для возврата экземпляра MagicMock, который упаковывает ваш класс mock, установив его в качестве возвращаемого значения, когда вызываемый объект пытается создать экземпляр module_a.A.

class SomeFncTestCase(unittest.TestCase):
  @unittest.mock.patch('module_a.A')
  def test_it(self, m):
    mock_a = MagicMock(wraps=MockA())
    m.return_value = mock_a

    module_a.some_fnc('in_file')

    m.assert_called_with('in_file')
    mock_a.get.assert_called()

Существует различие между m Mock, который по сути высмеивает A.__call__, и mock_a, поддельным экземпляром класса A (который предоставляет действительные возвращаемые значения функции путем переноса экземпляра MockA).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...