首页 > 解决方案 > Pytest unittest 函数不返回任何值

问题描述

您能帮我理解以下单元测试的问题吗?这是我正在为其编写单元测试的函数。

def running_config_from_database(device):
    try:
        data = databaseproxy(cluster='https://xxx.xxxx.xxx.net')
        datadb = 'test'
        query = f'''fGetrunningconfigData('{device}')
         '''
        raw_data = data.execute_query(datadb, query)
        # pdb.set_trace()
        for items in raw_data.fetchall():
            config = items['Config'].split('\r\n')
        for index, line in enumerate(config):
            if '$PASS$' in line:
                if line.startswith('set groups ospf_test'):
                    config_line = line.replace('$PASS$', get_auth('ospf'))
                    config[index] = config_line
                elif line.startswith('set groups rip_test'):
                    config_line = line.replace('$PASS$', get_auth('rsvp'))
                    config[index] = config_line
        config = config + overload_config
        return True, '\r\n'.join(config)
    except Exception as e:
        return False, f'Failed to get the running config from database, error: {e}'

这是我对此功能的单元测试:

@patch("scripts.test.overload_config")
@patch("scripts.test.get_auth")
@patch("scripts.test.databaseproxy.execute_query")
def test_running_config_from_database(self, mock_data, mock_cred, mock_overload):
    ret = MagicMock()
    ret.fetchall.return_value = [{'Hostname': 'devA', 'Config': 'set groups ospf_test secret $PASS$\r\n'}]
    mock_data.return_value = ret
    mock_cred.return_value = 'xyz'
    mock_overload = ['sample_overload_config1', 'sample_overload_config2']
    expected = ['set groups ospf_test secret xyz', '']
    out = expected + mock_overload
    data = '\r\n'.join(out) 
    status, out1 = tests.test_scripts.running_config_from_database('devA')
    assert status and out1 == data

当我运行这个 unittest 来测试函数时,我得到以下断言错误 - 看起来函数没有返回任何值。

@patch("scripts.test.overload_config")
@patch("scripts.test.get_auth")
@patch("scripts.test.databaseproxy.execute_query")
def test_running_config_from_database(self, mock_data, mock_cred, mock_overload):
        ret = MagicMock()
        ret.fetchall.return_value = [{'Hostname': 'devA', 'Config': 'set groups ospf_test secret $PASS$\r\n'}]
        mock_data.return_value = ret
        mock_cred.return_value = 'xyz'
        mock_overload = ['sample_overload_config1', 'sample_overload_config2']
        expected = ['set groups ospf_test secret xyz', '']
        out = expected + mock_overload
        data = '\r\n'.join(out) 
        status, out1 = tests.test_scripts.running_config_from_database('devA')
>       assert status and out1 == data
E       AssertionError: assert (True and '' == 'set groups d...rload_config2'
E         + set groups ospf_test secret xyz
E         + 
E         + sample_overload_config1
E         + sample_overload_config2)
tests/test_scripts.py:80: AssertionError

我编辑了我的函数以降低复杂性,但它仍然不起作用。不知道为什么。

Main Function:

===============

def running_config_from_database(device):
    try:
        pdb.set_trace()
        config = running_config_database(device)
        for index, line in enumerate(config):
            if '$PASS$' in line:
                if line.startswith('set groups ospf_test'):
                    config_line = line.replace('$PASS$', get_cred('ospf'))
                    config[index] = config_line
        config = config + overload_config
        return True, '\r\n'.join(config)
    except Exception as e:
        return False, f'Failed to get the running config from Database, error: {e}'

上述功能的单元测试结果:

=========================================================================================================== FAILURES ============================================================================================================
________________________________________________________________________________________________ test_running_config_from_database _________________________________________________________________________________________________

mock_cred = <MagicMock name='get_cred' id='140210277622336'>, mock_overload = ['sample_overload_config1', 'sample_overload_config2'], mock_running_config = <MagicMock name='running_config_database' id='140210277652128'>

    @patch("test.test1.scripts.running_config_database")
    @patch("test.test1.scripts.overload_config")
    @patch("test.test1.scripts.get_cred")
    def test_running_config_from_database(mock_cred, mock_overload, mock_running_config):
        mock_running_config.return_value = ['set groups ospf_test secret $PASS$', '']
        mock_cred.return_value = 'xyz'
        mock_overload = ['sample_overload_config1', 'sample_overload_config2']
        expected = ['set groups ospf_test secret xyz', '']
        out = expected + mock_overload
        data = '\r\n'.join(out)
        status, out1 = test.test1.scripts.test_running_config_from_database('devA')
>       assert status and out1 == data
E       AssertionError: assert (True and '' == 'set groups d...rload_config2'
E         + set groups ospf_test secret xyz
E         + 
E         + sample_overload_config1
E         + sample_overload_config2)

validation_tests/test_scripts.py:152: AssertionError
================================================================================================== 1 failed, 6 passed in 4.79s ==================================================================================================

标签: python-3.xpytest

解决方案


这里的问题是分配给mock_overload. 如果要调整模拟对象,则必须确保对象本身已更改。如果您只是分配另一个对象(在本例中为列表),您的变量现在指向列表对象,而原始对象mock_overload不再被引用(并且没有更改)。所以不要写:

mock_overload = ['sample_overload_config1', 'sample_overload_config2']

你可以例如写

mock_overload[:] = ['sample_overload_config1', 'sample_overload_config2']

为了清楚起见,这里是原始代码的简化版本:

>>> mock_overload = []
>>> id(mock_overload)
1477793866440
>>> mock_overload = [5, 6]
>>> id(mock_overload)
1477791015560  <- changed id, no longer pointing to the mock

现在与固定代码相同:

>>> mock_overload = []
>>> id(mock_overload)
140732764763024
>>> mock_overload[:] = [5, 6]
>>> id(mock_overload)
140732764763024  <- unchanged id, still points to the mock

请注意,这mock_overload[:] = [5, 6]基本上是一个快捷方式:

mock_object.clear()
mock_object.extend([5, 6])

推荐阅读