在测试类中模拟builtins.open会导致pytz出现问题

问题描述 投票:0回答:2

我发现自己无法在类中模拟打开文件,然后调用 pytz 来设置时区,因为 pytz 还需要执行文件打开操作,并且最终也收到模拟。问题的一个例子:

import unittest
from unittest.mock import patch, mock_open
from datetime import datetime
import pytz


class Foo:
    def __init__(self, filename):
        with open(filename, "r") as input:
            timestring = input.read()
            time = datetime.strptime(timestring, '%Y-%m-%d %H:%M:%S')
            zone = pytz.timezone('GMT')
            self.converted_time = zone.localize(time).strftime('%a %b %d %H:%M:%S %Z %Y')

    def show_time(self):
        return self.converted_time


class TestFoo(unittest.TestCase):
    def test_foo(self):
        with patch('builtins.open', mock_open(read_data="2023-12-20 00:00:00")) as mock_file:
            foo = Foo(mock_file)
            output = foo.converted_time
            self.assertEqual(output, 'Wed Dec 20 00:00:00 GMT 2023')


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

当达到

zone = pytz.timezone('GMT')
时测试失败,因为 pytz 还尝试从最初打开文件的模拟中读取数据。有没有办法将模拟限制为仅针对 Foo 类,而不是从其他模块调用的任何函数?或者,仅模拟builtins.open 的第一次使用?

python unit-testing mocking
2个回答
0
投票

最好重新设计你的类,不需要模拟来测试它。

Foo.__init__
应该采用类似文件的对象作为参数,而不是尝试打开文件本身。

import io


class Foo:
    def __init__(self, fobj):
        timestring = fobj.read()
        time = datetime.strptime(timestring, '%Y-%m-%d %H:%M:%S')
        zone = pytz.timezone('GMT')
        return zone.localize(time).strftime('%a %b %d %H:%M:%S %Z %Y')


class TestFoo(unittest.TestCase):
    def test_foo(self):
        output = Foo(io.StringIO("2023-12-20 00:00:00"))
        self.assertEqual(output, 'Wed Dec 20 00:00:00 GMT 2023')

0
投票

经过一些实验,我发现用

patch('__main__.open')
代替
patch('builtins.open')
可以避免替换 pytz 模块中对 open 的调用,从而允许单元测试正确执行而无需任何其他更改。话虽如此,chepner 的答案似乎是更好的做法,因为它将打开文件的关注点与转换日期字符串的关注点分开了。

© www.soinside.com 2019 - 2024. All rights reserved.