Уведомления

Группа в Telegram: @pythonsu

#1 Авг. 7, 2018 17:17:37

jon34
Зарегистрирован: 2016-02-14
Сообщения: 47
Репутация: +  0  -
Профиль   Отправить e-mail  

unittest mock

Добрый день.
У меня есть класс, который при помощи configparser.ConfigParser() парсит конфиги, я сейчас хочу написать тесты для своего класса. Проблемы у меня возникли, когда я разбирался как правильно писать тесты.
На сколько я понял правильно написанные тесты в идеале не должны обращаться к файловой системе и для того, чтобы это сделать я хотел замокать несколько методов класса ConfigParser, чтобы они просто статично отдавали нужные мне тестовые данные и с этим у меня возникли проблемы. Помогите пожалуйста примером кода, который делает то, что мне нужно.

Если упрощенно, то код у меня выглядит вот так. Мне нужно понять как мне замокать в классе ConfigParser методы read и get_option. Чтобы я мог тестировать SomeClass.

 class ConfigParser:
    def __init__(self, path):
        self.path = path
        self.parsed_data = []
     
    def read(self):
        with open(self.path) as f:
            for i in f:
                self.parsed_data.append(i.split(':'))    
     
    def get_option(self, index):
        return self.parsed_data[index]
     
 
class SomeClass:
    def __init__(self):
        self.parser = ConfigParser('./config.conf')
        self.parser.read()
     
    def get_data(self):
        # производит какие-то доп манипуляции и отдает данные
        return self.parser.get_option(1)

Заранее спасибо.

Отредактировано jon34 (Авг. 7, 2018 17:18:17)

Офлайн

#2 Авг. 8, 2018 01:41:27

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10031
Репутация: +  857  -
Профиль   Отправить e-mail  

unittest mock

Пример, как я мокал файл, открываемый на диске

  
import unittest
from unittest.mock import patch
 
 
class ReadFileGoodInput(unittest.TestCase):
 
    def setUp(self):
        self.p1 = patch('os.path')
        self.p2 = patch('checkmail.open', create=True)
        self.m1 = self.p1.start()
        self.m1.exists.return_value = True
        self.m1.isfile.return_value = True
        self.m2 = self.p2.start()
        self.open_iter = \
            self.m2.return_value. \
            __enter__.return_value.__iter__
 
    def tearDown(self):
        self.p1.stop()
        self.p2.stop()
 
    def test_field_contents_in_one_line(self):
 
        def checkl(ls):
            self.open_iter.reset_mock()
            self.open_iter.return_value = ls
 
            afh = AccFileHandler('prog', 'version',
                                 'filename',
                                 'utf-8', '#', ':', 5,
                                 'password', 4)
            afh.start()
            afh.load_accounts()
            afh.end()
 
        lst = (
           '::::',
           'a::::',
           ':b:::',
           '::c::',
           ':::d:',
           '::::e',
 
           'a:b:::',
           'a:b:c::',
           'a:b:c:d:',
           'a:b:c:d:e',
 
           'aaa:bbb:ccc:ddd:eee',
           'aa aa:bb bb:cc cc:dd dd:ee ee',
           ' aa : bb : cc : dd : ee '
        )
 
        for i in lst:
            try:
                checkl([i])
            except:
                self.fail(repr(i))
Может, это и не идеально, так как я сам это методом тыка нащупал, но так оно работает и даёт полный набор возможностей для подмены содержимого файла.

В данном случае AccFileHandler() открывает файл по передаваемому имени filename, проверяя сначала его наличие на диске, но я подменил саму функцию открытия файла в питоне так, чтобы она могла использоваться через оператор with. В результате мок имитирует файл на диске и при открытии его через with возвращает итератор строк.

То есть на время группы тестов патчатся сами модули питона, подменяясь моками, и потом эти моки настраиваются в каждом тесте и дальше данные можно пропускать через функцию, которая вместо методов модулей питона будет вызывать методы мока.


tags: mock



Отредактировано py.user.next (Авг. 8, 2018 01:56:56)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version