Модульное тестирование на Python: вложение поддельных объектов

#python-3.x #unit-testing #pytest #python-unittest #python-unittest.mock

#python-3.x #модульное тестирование #pytest #python-unittest #python-unittest.mock

Вопрос:

Я новичок в модульном тестировании на Python. Я пытаюсь модульно протестировать метод, который делает что-то вроде этого:

 MyClass:

    client : Client

    def __init__(client):
        self.client=client

    def method_to_test(self):
        images = self.client.get_stuff()
        image = images[0]
        result = []
        for region in image.regions:    
            result.append(region.region_id)
        return result
  

Я пробовал издеваться и вкладывать все задействованные классы: Client, Image и ImageRegion. Но в какой-то момент макет прерывается, и мой тест завершается неудачей. Есть ли способ издеваться над вещами, чтобы можно было сделать что-то вроде:

 def test_method_to_test():
   result = MyClass(mock_client).method_to_test()
   assert dummy_region_id in result
  

… или, возможно, лучшие способы тестирования в таких ситуациях с вложенными пользовательскими объектами?

Моя последняя попытка выглядит так:

 with patch('package.module.ImageRegion') as MockRegion:
    region = MockRegion()
    region.region_id.return_value = dummy_region_id 
        with patch('package.module.Image') as MockImage:
            image = MockImage()
            image.regions.return_value = [region]
                with patch('package.module.Client') as MockClient:
                    mock_client = MockClient()
                    mock_client.get_regions.return_value = [image]
                    result = MyClass(mock_client).method_to_test()
                    assert dummy_region_id in result
  

Ответ №1:

Вы можете создать объект с имитацией с помощью unittest.mock и использовать внедрение зависимостей для передачи объекта с MyClass имитацией.

Например.

myclass.py :

 class MyClass:

    def __init__(self, client):
        self.client = client

    def method_to_test(self):
        images = self.client.get_stuff()
        image = images[0]
        result = []
        for region in image.regions:
            result.append(region.region_id)
        return result
  

test_myclass.py :

 import unittest
from unittest.mock import Mock
from collections import namedtuple
from myclass_63948475 import MyClass

Image = namedtuple('Image', ['regions'])
Region = namedtuple('Region', 'region_id')


class TestMyClass(unittest.TestCase):

    def test_method_to_test(self):

        regions = [Region('1'), Region('2'), Region('3')]
        images = [Image(regions=regions)]
        mock_client = Mock()
        mock_client.get_stuff.return_value = images
        myclass = MyClass(mock_client)
        actual = myclass.method_to_test()
        mock_client.get_stuff.assert_called_once()
        self.assertEqual(actual, ['1', '2', '3'])


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

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

 coverage run /Users/ldu020/workspace/github.com/mrdulin/python-codelab/src/stackoverflow/63948475/test_myclass_63948475.py amp;amp; coverage report -m           
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Name                                                  Stmts   Miss  Cover   Missing
-----------------------------------------------------------------------------------
src/stackoverflow/63948475/myclass_63948475.py           10      0   100%
src/stackoverflow/63948475/test_myclass_63948475.py      18      0   100%
-----------------------------------------------------------------------------------
TOTAL                                                    28      0   100%
  

Комментарии:

1. Это было гениально!