测试一个函数是不是被调用

Posted

技术标签:

【中文标题】测试一个函数是不是被调用【英文标题】:Test that a function is called测试一个函数是否被调用 【发布时间】:2016-11-16 16:16:40 【问题描述】:

我正在我的应用程序中编写测试,以测试方法是否被调用。这是在 Python 3.4.3 和 pytest-2.9.2 中运行的。我是 PyTest 的新手,但对 RSpec 和 Jasmine 非常熟悉。我不确定如何设置测试,以便测试是否调用了 imaplib.IMAP4_SSL。

我的应用结构:

/myApp
  __init__.py
  /shared
    __init__.py
    email_handler.py
  /tests
    __init__.py
    test_email_handler.py

email_handler.py

import imaplib
def email_conn(host):
    mail = imaplib.IMAP4_SSL(host)  
    return mail;

到目前为止我的测试有什么: test_email_handler.py

import sys   
sys.path.append('.')  

from shared import email_handler 

def test_email_handler():   
     email_handler.email_conn.imaplib.IMAP4_SSL.assert_called_once 

这显然失败了。如何设置此测试以测试是否调用了 imaplib.IMAP4_SSL?或者有没有更好的方法在我的应用中设置测试套件,以便更有效地支持测试?

【问题讨论】:

你可能想看看pytest-mock。 @das-g 是的,这很有趣。但是如何申请这个用例?我认为这更像是一个间谍而不是一个模拟。我可以利用这个库来解决这个问题吗? 尽管有它们的名字,但 pytest-mock(以及 unittest.mockmock 包)提供的替代对象是 xUnit Patterns.com calls "Test Spy":它们捕获对它们的调用(“被测系统”)以供以后验证。 (不过,xUnit Patterns.com 所称的“模拟对象”也适用于您的情况。另请参阅 Mocks Aren't Stubs。) 【参考方案1】:

这是一个使用 Python 3.5.2 标准库中的 unittest.mock 的示例:

test_email_handler.py

import sys
from unittest import mock
sys.path.append('.')

from shared import email_handler

@mock.patch.object(email_handler.imaplib, 'IMAP4_SSL')
def test_email_handler(mock_IMAP4_SSL):
    host = 'somefakehost'
    email_handler.email_conn(host)
    mock_IMAP4_SSL.assert_called_once_with(host)

请注意@mock.patch.object 装饰器,它用一个模拟对象替换了 IMAP4_SSL,该模拟对象是作为参数添加的。 Mock 是一个强大的测试工具,对于新用户来说可能会很困惑。我推荐以下内容以供进一步阅读:

https://www.toptal.com/python/an-introduction-to-mocking-in-python

http://engineroom.trackmaven.com/blog/mocking-mistakes/

http://alexmarandon.com/articles/python_mock_gotchas/

【讨论】:

【参考方案2】:

你可以这样做:

email_handler.py

import imaplib

def email_conn(host):
    print("We are in email_conn()")
    mail = imaplib.IMAP4_SSL(host)
    print(mail)
    return mail;

test_email_handler.py

import sys   
sys.path.append('.')  

from shared import email_handler 

def test_email_handler():
    print("We are in test_email_handler()")
    email_handler.email_conn.imaplib.IMAP4_SSL.assert_called_once
    print(email_handler.email_conn.imaplib.IMAP4_SSL.assert_called_once) # this will give you the result of the function (in theory) 

基本上,您所做的是打印函数返回的内容。如果没有错误,该函数应该已经被执行了。

您还可以做的是修改 imaplib 的源代码,以便在您正在调用的函数中打印。

祝你好运!

【讨论】:

这个测试不起作用。我得到错误:AttributeError:'function'对象没有属性'imaplib'【参考方案3】:

这听起来像是代码覆盖率的问题:这一行是否被执行了?

对于python的覆盖工具是:https://coverage.readthedocs.io

Pytest 基于该工具构建的插件,非常方便: https://pypi.python.org/pypi/pytest-cov

【讨论】:

我确实想包含更多代码覆盖率信息。但是,测试失败,因为 test_email_handler 中的断言设置不正确。我在assert_called_once 行收到错误消息。 @analyticsPierce 看看这个问题的答案是否会有所帮助:***.com/questions/3829742/…。还有github.com/pytest-dev/pytest-mock 可能提供需要的功能 我已经看到了这个问题,但我认为它是 unittest 而不是 py.test。我也使用了类似的断言条件,但它们都失败了。这就是为什么我添加了赏金以便我可以获得一个清晰的代码示例。是的,我知道 pytest-mock。我的问题是如何利用它来解决这个问题。

以上是关于测试一个函数是不是被调用的主要内容,如果未能解决你的问题,请参考以下文章

如何安全地测试一个方法是不是可以通过 NSInvocation 被调用

如何检查一个函数是不是被另一个函数调用?

GHUnit,OCMock:如何测试两个指定块之一是不是被调用?

Unity 单元测试 - 检查带有参数的函数调用是不是引发异常

如何测试函数是不是使用 async 关键字调用

Angular - 单元测试间谍无法识别该函数已被调用