Python 模拟 returns 无效值

Python mock returns invalid value

我有以下结构:

sources/
   - parser/
      - sources_parser.py # SourcesParser class is here
      - tests/
         - test_sources_parsers.py # SourcesParserTest is here

sources_parser.py:

from sources.parser.sitemap_parser import SitemapParser

class SourcesParser(object):

    __sitemap_parser: SitemapParser

    def __init__(self) -> None:
        super().__init__()

        self.__sitemap_parser = SitemapParser()

    def parse(self):
        # ... 
        urls = self.__parse(source)
        self.logger.info(f'Got {len(urls)} URLs')

    def __parse(self, source: NewsSource) -> List[str]:
        results = self.__sitemap_parser.parse_sitemap(source.url)
        self.logger.info(f'Results: {results}, is list: {isinstance(results, list)}')
        return results

测试:

class SourcesParserTest(TestCase):

    @patch('sources.parser.sources_parser.SitemapParser')
    def test_normal_parse(self, mock_sitemap_parser):
        mock_sitemap_parser.parse_sitemap.return_value = [
            'https://localhost/news/1',
            'https://localhost/news/2',
            'https://localhost/news/3',
        ]

        parser = SourcesParser()
        parser.parse()

日志是:

Results: <MagicMock name='SitemapParser().parse_sitemap()' id='5105954240'>, is list: False
Got 0 URLs

如果我理解正确,parse_sitemap 调用应该 return 给定的 URL 列表,而是 return 一个 Mock 对象,该对象被转换为空列表。

Python 版本是 3.9.

怎么了?

如果模拟一个成员函数,你必须模拟模拟实例对象上的函数,而不是模拟 class 对象上的函数。这可能不完全直观,因为成员函数属于 class,但您可以将其视为绑定方法与未绑定方法 - 您必须模拟绑定方法。

使用 Mock,通过在 class 对象上使用 return_value 来模拟实例,类似于模拟函数调用的结果,因此在您的情况下,您需要:

    @patch('sources.parser.sources_parser.SitemapParser')
    def test_normal_parse(self, mock_sitemap_parser):
        mocked_parser = mock_sitemap_parser.return_value  # mocked instance
        mock_parser.parse_sitemap.return_value = [
            'https://localhost/news/1',
            'https://localhost/news/2',
            'https://localhost/news/3',
        ]
        ...

(拆分模拟仅供参考)