Python unittest: невозможно смоделировать импортированные функции, чтобы условное вычисление имело значение False - PullRequest
0 голосов
/ 10 апреля 2020

У меня проблема с модульным тестированием в Python. В частности, когда я пытаюсь смоделировать функцию, которую импортирует мой код, переменные, назначенные на выход этой функции, присваиваются объекту MagicMock вместо return_value функции mock-функции. Я копался в документах для библиотеки python unittest, но мне не повезло.

Ниже приведен код, который я хочу проверить:

from production_class import function_A, function_B, function_M

class MyClass:
    def do_something(self):
        variable = functionB()
        if variable:
            do_other_stuff()
        else:
            do_something_else

this вот что я пробовал:

@mock.patch(path.to.MyClass.functionB)
@mock.patch(<other dependencies in MyClass>)
def test_do_something(self, functionB_mock):
    functionB_mock.return_value = None # or False, or 'foo' or whatever.
    myClass = MyClass()
    myClass.do_something()
    self.assertTrue(else_block_was_executed)

Проблема, с которой я столкнулся, заключается в том, что когда в MyClass тест достигает значения variable = functionB, переменная не получает моего возвращаемого значения; он устанавливается на объект MagicMock (и поэтому оператор if всегда оценивается как True). Как мне смоделировать импортированную функцию таким образом, чтобы при ее выполнении переменным фактически присваивалось возвращаемое значение, а не сам объект MagicMock?

Ответы [ 2 ]

0 голосов
/ 13 апреля 2020

В итоге я изменил операторы импорта в MyClass для импорта объекта вместо отдельных методов. Затем я смог без проблем смоделировать объект.

Более конкретно, я изменил MyClass так:

import production_class as production_class

class MyClass:
    def do_something(self):
        variable = production_class.functionB()
        if variable:
            do_other_stuff()
        else:
            do_something_else

и изменил свой тест на

@mock.patch(path.to.MyClass.production_class)
def test_do_something(self, prod_class_mock):
    prod_class_mock.functionB.return_value = None
    myClass = MyClass()
    myClass.do_something()
    self.assertTrue(else_block_was_executed)
0 голосов
/ 10 апреля 2020

Нам нужно посмотреть, какой путь импорта вы на самом деле используете с path.to.MyClass.functionB. При манипулировании объектами вы не обязательно используете путь непосредственно к месту расположения объекта, но тот, который интерпретатор видит при рекурсивном импорте модулей.

Например, если ваш тест импортирует MyClass из myclass.py, и этот файл импортирует functionB из production_class.py, путь макета будет myclass.functionB вместо production_class.functionB.

Тогда возникает проблема, которая требует дополнительных макеты MyClass.do_other_stuff и MyClass.do_something_else, чтобы проверить, вызвал ли MyClass правильный нисходящий метод на основе возвращаемого значения functionB.

. Вот рабочий пример, который проверяет оба возможных возвращаемых значения functionB и вызывают ли они правильный нисходящий метод:

myclass.py

from production_class import functionA, functionB, functionM


class MyClass:
    def do_something(self):
        variable = functionB()
        if variable:
            self.do_other_stuff()
        else:
            self.do_something_else()

    def do_other_stuff(self):
        pass

    def do_something_else(self):
        pass

production_class.py

import random

def functionA():
    pass

def functionB():
    return random.choice([True, False])

def functionM():
    pass

test_myclass.py

import unittest
from unittest.mock import patch
from myclass import MyClass


class MyTest(unittest.TestCase):

    @patch('myclass.functionB')
    @patch('myclass.MyClass.do_something_else')
    def test_do_something_calls_do_something_else(self, do_something_else_mock, functionB_mock):
        functionB_mock.return_value = False
        instance = MyClass()
        instance.do_something()
        do_something_else_mock.assert_called()


    @patch('myclass.functionB')
    @patch('myclass.MyClass.do_other_stuff')
    def test_do_something_calls_do_other_stuff(self, do_other_stuff_mock, functionB_mock):
        functionB_mock.return_value = True
        instance = MyClass()
        instance.do_something()
        do_other_stuff_mock.assert_called()


if __name__ == '__main__':
    unittest.main()

вызов python test_myclass.py приводит к:

..
----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK
...