【问题标题】:Why is my class not getting mocked in my unittest?为什么我的班级在我的单元测试中没有被嘲笑?
【发布时间】:2021-08-26 10:16:23
【问题描述】:

我正在尝试模拟 GetDatabaseConnection,但它仍在其中运行代码。

class GetDatabaseConnection:
    resp_dict = json.loads(get_secret())
    endpoint = resp_dict.get('host')
    username = resp_dict.get('username')
    password = resp_dict.get('password')
    database_name = resp_dict.get('dbname')
    port = resp_dict.get('port')
    connection = pymysql.connect(host=endpoint, user=username, passwd=password, db=database_name, port=port)
    cursor = connection.cursor()

这是我编写的用来模拟类的测试。

    @mock.patch("lambda_function.GetDatabaseConnection")
    def test_mock_simple_class(mock_class):
        mock_class.return_value = "test"

但我收到以下错误

test_lambda_function.py::TestPreSignUp::test_mock_simple_class FAILED    [100%]
test_lambda_function.py:151 (TestPreSignUp.test_mock_simple_class)
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/unittest/mock.py:1334: in patched
    with self.decoration_helper(patched,
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/contextlib.py:117: in __enter__
    return next(self.gen)
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/unittest/mock.py:1316: in decoration_helper
    arg = exit_stack.enter_context(patching)
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/contextlib.py:429: in enter_context
    result = _cm_type.__enter__(cm)
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/unittest/mock.py:1389: in __enter__
    self.target = self.getter()
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/unittest/mock.py:1564: in <lambda>
    getter = lambda: _importer(target)
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/unittest/mock.py:1236: in _importer
    thing = __import__(import_path)
../lambda_function.py:42: in <module>
    class GetDatabaseConnection():
../lambda_function.py:43: in GetDatabaseConnection
    resp_dict = json.loads(get_secret())

【问题讨论】:

    标签: python mysql unit-testing python-unittest


    【解决方案1】:

    您遇到的问题与关于importing without executing the class - python 的其他问题相同。由于您将类设计为具有可以执行对pymysql 的调用的属性,因此即使刚刚导入文件(例如在模拟修补期间),它们也会立即执行,甚至无需创建GetDatabaseConnection 的实例。

    src.py

    import pymysql
    
    
    class GetDatabaseConnection:
        connection = pymysql.connect(host="127.0.0.1", user='username', passwd="password", db='database_name', port=80)
    

    test_src.py

    from unittest import mock
    
    # This will read your class. And this will run your <pymsql> commands even without a running patch yet.
    import src  # or <from src import GetDatabaseConnection>
    
    
    # This will also read your class with or without the import above. And this will run your <pymsql> commands even without the patch yet.
    @mock.patch('src.GetDatabaseConnection')
    def test_try1():
        assert True
    

    输出

    $ pytest -q -rP
    
    ================================================================================================= ERRORS ==================================================================================================
    ______________________________________________________________________________________ ERROR collecting test_samp.py
    E   pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '127.0.0.1' ([Errno 111] Connection refused)")
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    1 error in 0.22s
    

    解决方案 1:

    通过将类放在类方法中,重新设计类以不执行类级逻辑。

    class GetDatabaseConnection:
        def __init__(self):
            self.connection = pymysql.connect(host="127.0.0.1", user='username', passwd="password", db='database_name', port=80)
    
    from unittest import mock
    
    
    @mock.patch('src.GetDatabaseConnection')
    def test_try(mock_class):
        assert True
    
    $ pytest -q -rP
    ================================================================================================= PASSES ==================================================================================================
    1 passed in 0.03s
    

    解决方案 2:

    将您的GetDatabaseConnection 转换为普通函数:

    def getDatabaseConnection():
        return {
            "connection": pymysql.connect(host="127.0.0.1", user='username', passwd="password", db='database_name', port=80),
        }
    
    from unittest import mock
    
    
    @mock.patch('src.getDatabaseConnection')
    def test_try(mock_func):
        assert True
    
    • 输出与解决方案 1 相同

    解决方案 3:

    不推荐。不要在没有运行补丁的情况下直接导入文件。因此,也不要修补类GetDatabaseConnection 以避免从文件中读取并执行pymysql。首先,您需要在导入文件之前修补pymysql。这很难维护,如果您的源代码文件之一导入包含GetDatabaseConnection 类的文件,则会中断。

    from unittest import mock
    
    
    @mock.patch('pymysql.connect')
    def test_try(mock_pymsql):
        import src  # Or <from src import GetDatabaseConnection>
        print(f"{src.GetDatabaseConnection.connection=}")  # This will be the patched version
        assert True
    

    输出:

    $ pytest -q -rP
    ================================================================================================= PASSES ==================================================================================================
    ________________________________________________________________________________________________ test_try _________________________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    src.GetDatabaseConnection.connection=<MagicMock name='connect()' id='139647536711760'>
    1 passed in 0.03s
    

    【讨论】:

      猜你喜欢
      • 2015-06-06
      • 2016-09-12
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 2021-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多