Как можно замаскировать (заблокировать) метод __next__?

Как исправить код, чтобы метод `__next__` был обернут в метод `Queue`, позволяющий мокирование с помощью `MagicMock`, и был вызван в тесте?
  • 13 ноября 2023 г. 18:00
Ответы на вопрос 2
Чтобы замаскировать (заблокировать) метод `__next__`, можно воспользоваться декоратором `patch` из модуля `unittest.mock`. Для этого необходимо импортировать `patch` и `MagicMock`, а затем применить `patch` к методу `__next__`, указав мок (mock) в качестве замещающего объекта.

Вот пример:

```python
from unittest.mock import patch, MagicMock

# Определяем класс с методом __next__
class MyIterator:
    def __init__(self):
        self.data = [1, 2, 3]
        self.index = 0
    
    def __next__(self):
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

# Тест для метода __next__
def test_next():
    iterator = MyIterator()
    
    # Замаскировать (заблокировать) метод __next__
    with patch.object(iterator, '__next__', MagicMock(return_value=999)):
        result = next(iterator)
        assert result == 999, "Неверный результат"
```

Чтобы исправить код так, чтобы метод `__next__` был обернут в метод `Queue`, следует создать класс `Queue`, который будет оборачивать экземпляр класса `MyIterator` и предоставлять доступ к методу `__next__` через собственный метод `next`. Затем можно замаскировать (заблокировать) метод `next`, чтобы использовать его в тесте.

Вот исправленный пример кода:

```python
from unittest.mock import patch, MagicMock

# Определяем класс с методом __next__
class MyIterator:
    def __init__(self):
        self.data = [1, 2, 3]
        self.index = 0
    
    def __next__(self):
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

# Определяем класс-обертку Queue
class Queue:
    def __init__(self):
        self.iterator = MyIterator()
        
    def next(self):
        return next(self.iterator)

# Тест для метода __next__ через метод Queue.next
def test_next():
    queue = Queue()
    
    # Замаскировать (заблокировать) метод next
    with patch.object(queue, 'next', MagicMock(return_value=999)):
        result = queue.next()
        assert result == 999, "Неверный результат"
```

Таким образом, метод `__next__` теперь обернут в метод `Queue.next`, который можно замаскировать и вызвать в тесте.
Вместо return self из метода __iter__() рекомендуется вернуть итератор по внутренней коллекции, например iter(self.nums). Такой подход логичен, так как при чтении коллекции в цикле for не должно происходить ее модификации. Для этого стоит создать отдельный метод. Еще можно превратить метод __iter__() в генератор, используя yield для возврата значений. В обоих случаях нет необходимости определять метод __next__().
Похожие вопросы