Python的MiniMock可以创建同文件内函数的mock吗?

3 投票
2 回答
1379 浏览
提问于 2025-04-15 14:31

我正在使用Python的MiniMock库来进行单元测试。我想要模拟一个在和我的doctest同一个Python文件中定义的函数。MiniMock能做到这一点吗?我尝试的简单方法失败了:

def foo():
    raise ValueError, "Don't call me during testing!"

def bar():
    """
    Returns twice the value of foo()

    >>> from minimock import mock
    >>> mock('foo',returns=5)
    >>> bar()
    Called foo()
    10

    """
    return foo() * 2

if __name__ == "__main__":
    import doctest
    doctest.testmod()

如果我尝试运行这段代码,会发生这样的事情:

**********************************************************************
File "test.py", line 9, in __main__.bar
Failed example:
    bar()
Exception raised:
    Traceback (most recent call last):
      File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/doctest.py", line 1212, in __run
        compileflags, 1) in test.globs
      File "<doctest __main__.bar[2]>", line 1, in <module>
        bar()
      File "test.py", line 13, in bar
        return foo() * 2
      File "test.py", line 2, in foo
        raise ValueError, "Don't call me!"
    ValueError: Don't call me!
**********************************************************************
1 items had failures:
   1 of   3 in __main__.bar
***Test Failed*** 1 failures.

编辑:根据下面的回答,这被识别为一个bug,并且已经在MiniMock中修复了。

2 个回答

1

这个可以用:

def foo():
    raise ValueError, "Don't call me during testing!"

def bar():
    """
    Returns twice the value of foo()

    >>> from minimock import mock
    >>> mock('foo',returns=5)
    >>> bar.func_globals['foo'] = foo
    >>> bar()
    Called foo()
    10

    """
    return foo() * 2

if __name__ == "__main__":
    import doctest
    doctest.testmod()

看起来在进行模拟的时候,bar里的foo已经和原始函数绑定在一起了。

这是因为在运行文档测试时,doctest模块是在一个模块全局命名空间的副本中运行的,但bar的全局变量还是保持原样。所以mock函数改变了副本命名空间中的foo,但是bar依然在看着原来的那个。

我不知道有没有更好的方法来解决这个问题。

编辑 2:我收回之前的话。MiniMock是专门为文档测试设计的。我怀疑你发现了一个bug。

编辑:我想推荐的做法是,在开始测试之前先设置好模拟,像这样:

def foo():
    raise ValueError, "Don't call me during testing!"

def bar():
    """
    Returns twice the value of foo()

    >>> bar()
    10

    """
    return foo() * 2

if __name__ == "__main__":
    from minimock import mock
    mock('foo',returns=5)
    import doctest
    doctest.testmod()

这样“调用foo()”的消息也不会出现在文档测试中。

5

我刚在邮件列表上回复了一个MiniMock的补丁,这个补丁可以解决这个问题。

在这个补丁应用之前,你可以用以下两行代码替代itsadok的代码片段:

>>> mock('foo',returns=5)
>>> bar.func_globals['foo'] = foo

你也可以使用

>>> mock('foo', nsdicts=(bar.func_globals,), returns=5)

撰写回答