在导入模块中 Python 单元测试中模拟文件

Mocking Files In Python Unittest In Imported Modules

我欣然承认在单元测试方面有点过火了。 当我通过测试时,我发现我的解决方案不够优雅,我很好奇是否有人有更简洁的解决方案。

正在测试的class:

class Config():

    def __init__(self):
        config_parser = ConfigParser()
        try:
            self._read_config_file(config_parser)
        except FileNotFoundError as e:
            pass

        self.token = config_parser.get('Tokens', 'Token', )

    @staticmethod
    def _read_config_file(config):
        if not config.read(os.path.abspath(os.path.join(BASE_DIR, ROOT_DIR, CONFIG_FILE))):
            raise FileNotFoundError(f'File {CONFIG_FILE} not found at path {BASE_DIR}{ROOT_DIR}')

丑陋的测试:

class TestConfiguration(unittest.TestCase):

    @mock.patch('config.os.path.abspath')
    def test_config_init_sets_token(self, mockFilePath: mock.MagicMock):
        with open('mock_file.ini', 'w') as file: #here's where it gets ugly
            file.write('[Tokens]\nToken: token')
        mockFilePath.return_value = 'mock_file.ini'

        config = Config()

        self.assertEqual(config.token, 'token')
        os.remove('mock_file.ini') #quite ugly

编辑:我的意思是我正在创建一个文件而不是模拟一个文件。 有谁知道如何 mock 文件对象,同时设置其数据以便读取 ascii 文本? class 深埋。 除此之外,ConfigParser 使用 .read() 设置数据的方式让我很反感。诚然,测试 "works",它做的并不好。

对于那些询问其他测试行为的人,这里是另一个测试的例子class:

@mock.patch('config.os.path.abspath')
def test_warning_when_file_not_found(self, mockFilePath: mock.MagicMock):
    mockFilePath.return_value = 'mock_no_file.ini'

    with self.assertRaises(FileNotFoundError):
        config.Config._read_config_file(ConfigParser())

感谢您的宝贵时间。

找到了!

我不得不从一些导入开始:from io import TextIOWrapper, BytesIO

这允许创建文件对象:TextIOWrapper(BytesIO(b'<StringContentHere>'))

下一部分涉及深入研究 configparser 模块以查看它调用 open(),以便 mock.patch 行为,这里我们有它,一个独立的单元测试!

@mock.patch('configparser.open') 
def test_bot_init_sets_token(self, mockFileOpen: mock.MagicMock):

    mockFileOpen.return_value = TextIOWrapper(BytesIO(b'[Tokens]\nToken: token'))

    config = Config()

    self.assertEqual(config.token, 'token')