对使用上下文管理器的方法进行单元测试

时间:2018-10-02 15:34:05

标签: python-3.x unit-testing testing mocking

我有一种方法要进行单元测试。该方法需要一个文件路径,然后使用上下文管理器打开该文件路径,以解析一个返回值(如果存在的话),该值应足够简单。

@staticmethod
def read_in_target_language(file_path):
    """
    .. note:: Language code attributes/values can occur
    on either the first or the second line of bilingual.
    """
    with codecs.open(file_path, 'r', encoding='utf-8') as source:
        line_1, line_2 = next(source), next(source)
        get_line_1 = re.search(
            '(target-language=")(.+?)(")', line_1, re.IGNORECASE)
        get_line_2 = re.search(
            '(target-language=")(.+?)(")', line_2, re.IGNORECASE)
        if get_line_1 is not None:
            return get_line_1.group(2)
        else:
            return get_line_2.group(2)

出于明显的原因,我想避免针对外部文件进行测试,并且不希望创建临时文件。另外,在这种情况下,我不能使用StringIO。

如何在单元测试用例中模拟file_path对象?最终,我需要创建一个包含不同值的模拟路径。感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

(免责声明:我不会说Python,所以我很可能会在细节上犯错)

我建议您改为嘲笑codecs。使模拟的open方法返回一个对象,其中包含要从read调用中返回的测试数据。这可能涉及为返回值创建另一个模拟对象。我不知道您是否可以在Python中使用某些股票类代替。

然后,为了实际启用测试逻辑,请向read_in_target_language添加一个参数,该参数表示可以承担原始codecs对象角色的对象,即,按参数进行依赖项注入。为方便起见,我想您可以将其默认设置为codecs

我不确定Python的鸭子类型在静态和实例方法方面能走多远,但是类似这样的东西应该可以使您大致了解:

def read_in_target_language(file_path, opener=codecs):
    ...
    with opener.open(file_path, 'r', encoding='utf-8') as source:

如果上述操作不可行,则可以添加一个间接层:

class CodecsOpener:
    ...
    def open(self, file_path, access, encoding):
        return codecs.open(file_path, access, encoding)

class MockOpener:
    ...
    def __init__(self, open_result):
        self.open_result = open_result

    def open(self, file_path, access, encoding):
        return self.open_result

...

    def read_in_target_language(file_path, opener=CodecsOpener()):
        ...
        with opener.open(file_path, 'r', encoding='utf-8') as source:
            ...

...

    def test():
        readable_data = ...
        opener = MockOpener(readable_data)
        result = <class>.read_in_target_language('whatever', opener)
        <check result>