Используя библиотеки тестов модулей Python (unittest, mock), как утверждать, был ли вызван метод класса B внутри метода класса A? - PullRequest
0 голосов
/ 30 августа 2018

При условии следующей настройки:

class A:
    def __init__(self, nodes):
        self.nodes=nodes

    def update(self, bool_a=True):
        if bool_a:
            for n in self.nodes:
                if hasattr(self.nodes[n], 'update'):
                    self.nodes[n].update()

class B:
    def __init__(self, int_attr=5):
        self.int_attr=int_attr

    def update(self):
        self.int_attr = 0

Предположим, что список узлов в классе A фактически является списком экземпляров класса B.

Как мне написать модульный тест для метода обновления класса A, чтобы проверить, был ли вызван метод обновления каждого узла класса B, содержащегося в self.nodes класса A?

В более общей настройке предположим, что существует несколько классов, реализующих метод обновления, и они могут быть узлами в пределах self.nodes класса A. Как проверить, что были вызваны все методы обновления членов self.nodes?

Я безуспешно пробовал следующее:

mock_obj = MagicMock()
@patch('module.A.update', return_value=mock_obj)
def test_update(self, mock_obj):
    nodes = {}
    nodes['first'] = B(int_attr=1)
    nodes['second'] = B(int_attr=2)
    test_A = module.A(nodes=nodes)
    test_A.update(bool_A=True)
    self.assertTrue(mock_obj.called)

как предложено в насмешка над функцией в методе класса .

Редактировать: Если мы примем этот конкретный случай:

import unittest
import mock
from unittest import TestCase

class A:
    def __init__(self, nodes):
        self.nodes=nodes

    def update(self, bool_a=True):
        if bool_a:
            to_update = [n for n in self.nodes]
            while len(to_update) > 0:
                if hasattr(self.nodes[to_update[-1]], 'update'):
                    self.nodes[to_update[-1]].update()
                    print('Update called.')
                    if self.nodes[to_update[-1]].is_updated:
                        to_update.pop()

class B:
    def __init__(self, int_attr=5):
        self.int_attr=int_attr
        self.is_updated = False

    def update(self):
        self.int_attr = 0
        self.is_updated = True

class TestEnsemble(TestCase):
    def setUp(self):
        self.b1 = B(1)
        self.b2 = B(2)
        self.b3 = B(3)
        self.nodes = {}
        self.nodes['1'] = self.b1
        self.nodes['2'] = self.b2
        self.nodes['3'] = self.b3
        self.a = A(self.nodes)

    @mock.patch('module.B.update')
    def test_update(self, mock_update):
        mock_update.return_value = None
        self.a.update()
        with self.subTest():
            self.assertEqual(mock_update.call_count, 3)

Запуск unittest для этого случая приводит к бесконечному циклу, поскольку атрибут is_updated никогда не устанавливается в True, поскольку метод обновления класса B является ложным. Как измерить количество времени, в течение которого B.update был вызван в A.update?

Обновление: Пробовал это:

@mock.patch('dummy_script.B')
def test_update(self, mock_B):
    self.a.update()
    with self.subTest():
        self.assertEqual(mock_B.update.call_count, 3)

Функция обновления теперь действительно запускается 3 раза (я вижу это в выводе консоли, так как «Обновление вызвано.» Выводится три раза), но call_count метода update остается нулевым. Я проверяю неправильный атрибут / объект?

Ответы [ 2 ]

0 голосов
/ 03 сентября 2018

Объединяя ответы от @Gang и @jonrsharpe, следующие фрагменты кода решают вопросы, которые я задавал выше:

Как написать модульный тест для TestA.test_update (), чтобы узнать, был ли вызван B.update ()? Смотрите ответ @Gangs.

Как проверить, чтобы все B.update () были вызваны для всех узлов A.? 1008 * Смотрите ответ @Gangs.

Запуск в бесконечный цикл, когда B.is_udpated не смоделирован после того, как OP обновил код

Как предложил @jonrsharpe, здесь решение состоит в том, чтобы создать один фиктивный объект на экземпляр B в узлах и отдельно проверять вызовы функций:

class TestA(TestCase):

    @mock.patch('module.B')
    @mock.patch('module.B')
    @mock.patch('module.B')
    def test_update(self, mock_B1, mock_B2, mock_B3):
        nodes = {}
        nodes['1'] = mock_B1
        nodes['2'] = mock_B2
        nodes['3'] = mock_B3
        a = A(nodes)
        a.update()
        with self.subTest():
            self.assertEqual(mock_B1.update.call_count, 1)
        with self.subTest():
            self.assertEqual(mock_B2.update.call_count, 1)
        with self.subTest():
            self.assertEqual(mock_B3.update.call_count, 1)

Кроме того, если вы хотите по какой-то причине выполнить фиктивные функции (в случае, если они устанавливают некоторые флаги или переменные, которые влияют на время выполнения), можно написать такой тест:

def test_fit_skip_ancestors_all(self):
    nodes = {}
    nodes['1'] = mock_B1
    nodes['2'] = mock_B2
    nodes['3'] = mock_B3
    a = A(nodes)
    with mock.patch.object(A.nodes['1'],'update',wraps=A.nodes['1'].update) as mock_B1, \
mock.patch.object(A.nodes['2'], 'update', wraps=A.nodes['2'].update) as mock_B2, \
mock.patch.object(A.nodes['3'], 'update', wraps=A.nodes['3'].update) as mock_B3:

    a.update()
    with self.subTest():
        self.assertEqual(mock_B1.call_count, 1)
    with self.subTest():
        self.assertEqual(mock_B2.call_count, 1)
    with self.subTest():
        self.assertEqual(mock_B3.call_count, 1)
0 голосов
/ 31 августа 2018

Как написать модульный тест для TestA.test_update(), чтобы узнать, был ли вызван B.update()?

Это просто для того, чтобы дать некоторые идеи.

import mock
import unittest
import A
import B

class TestB(unittest.TestCase):

    # only mock away update method of class B, this is python2 syntax
    @mock.patch.object(B, 'update')
    def test_update(self, mockb_update):
        # B.update() does not return anything
        mockb_update.return_value = None
        nodes = {}
        nodes['first'] = B(int_attr=1)
        nodes['second'] = B(int_attr=2)
        test_A = A(nodes)
        test_A.update(bool_A=True)
        self.assertTrue(mockb_update.called)

Как проверить, что все B.update() были вызваны для всех A.nodes?

    # same everthing except this
    self.assertEqual(mockb_update.call_count, 2)

Запуск в бесконечный цикл, когда B.is_udpated не подвергается насмешкам после того, как OP обновил код

макет B.is_updated внутри __init__ или фиктивный класс __init__ - тема более сложная, чем оригинальный пост

Вот несколько мыслей: B.is_updated не может быть просто mock.patch, оно доступно только после инициации класса B. так что опция

а) макет B.__init__ или конструктор класса

б) высмеивать весь класс B, что проще в вашем случае, установите is_updated равным True, и вы закончите бесконечный цикл.

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