9

我正在为我的 python 2.7 方法之一编写一个单元测试用例。

在我的测试方法中,有一个方法调用将带有字符串键和 panadas 数据框的字典作为该键的值。

我想为此方法编写一个交互测试,以检查它是否使用正确的字典在内部调用该方法

def MethodUnderTest(self):
    #some code here
    externalMethod(dictionary_of_string_dataframe)
    #some code here

在单元测试中,我确实写了我的断言来测试这样的交互

mock_externalClass.externalMethod.assert_called_once_with(dictionary_of_string_dataframe) 

我创建 dictionary_of_string_dataframe 的方式与在实际方法中创建的方式完全相同。事实上,我复制了在测试代码中执行此操作的辅助方法,只是为了确保两个字典相同。我什至在 python 控制台上调试测试方法时打印了这两个字典,它们看起来完全一样。

我使用@patch 装饰器修补外部类,一切正常。

问题是在上面提到的断言语句中,我收到以下错误:

 mock_externalClass.externalMethod.assert_called_once_with(dictionary_of_string_dataframe)
  File "C:\Python27\lib\site-packages\mock\mock.py", line 948, in assert_called_once_with
    return self.assert_called_with(*args, **kwargs)
  File "C:\Python27\lib\site-packages\mock\mock.py", line 935, in assert_called_with
    if expected != actual:
  File "C:\Python27\lib\site-packages\mock\mock.py", line 2200, in __ne__
    return not self.__eq__(other)
  File "C:\Python27\lib\site-packages\mock\mock.py", line 2196, in __eq__
    return (other_args, other_kwargs) == (self_args, self_kwargs)
  File "C:\Python27\lib\site-packages\pandas\core\generic.py", line 953, in __nonzero__
    .format(self.__class__.__name__))
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

我确实搜索了 valueError 但没有太多帮助。有人可以告诉我这里发生了什么吗?

我确实检查了以下问题,但这没有帮助

ValueError:DataFrame 的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()

4

4 回答 4

8

我在尝试测试我编写的函数是否使用某个预处理数据帧调用时也遇到了这个问题,我使用mock's call_args 属性和pandas's解决了这个问题testing.assert_frame_equal

在我的情况下,我想断言作为第二个参数传递给我下面调用的函数的数据帧的值,该函数score_function由更高级别的函数调用run_scoring。所以首先我检索了*args对模拟使用的方法调用的一部分,[0]然后我得到了我的第二个位置参数(这是我想要断言的值)使用[1].

然后我可以使用pd.testing.assert_frame_equal.

from unittest.mock import Mock
import pandas as pd

import my_code

...

score_function_mocked = Mock()
my_code.score_function = score_function_mocked
my_code.run_scoring()

pd.testing.assert_frame_equal(
    # [0] = *args, [0][1] = second positional arg to my function
    score_function_mocked.call_args[0][1],
    pd.DataFrame({
        'new_york': [1, 0],
        'chicago': [0, 1],
        'austin': [0, 0]
    })

)
于 2019-02-18T18:10:24.240 回答
4

==发生这种情况是因为 unittest.mock 使用or比较输入值!=。但是,pandas 的 dataframe 无法进行类似的比较,而必须使用.equalsDataFrames 的方法。

https://github.com/testing-cabal/mock/blob/master/mock/mock.py

一种可能的解决方法是编写您自己的单元测试,该测试遍历字典并使用该.equals方法在数据帧之间进行比较。

另一个是覆盖__equals__pandas 数据框的方法,因此mock在它们之间进行比较时,它将使用正确的方法。

于 2017-06-19T22:27:04.717 回答
3

这是我根据原始问题解决它的方法。这借用了 sfogle 的回答中的一些想法,但它允许您通常测试调用的函数,无论 args 是否为 DataFrames。

import unittest
from unittest.mock import patch
import pandas as pd

def external_method(df):
    return df

def function_to_test(df):
    external_method(df)
    return df

class MyTest(unittest.TestCase):

    def test_the_function_to_test(self):
        my_test_df = pd.DataFrame({"a": [1, 2, 3, 4]})    
        with patch(__name__ + ".external_method") as mock_external_method:        
            function_to_test(my_test_df)
            mock_external_method.assert_called_once()
            args, kwargs = mock_external_method.call_args
            self.assertTrue(args[0].equals(my_test_df))
于 2019-10-22T18:33:34.033 回答
3

我通过有一个 SAME_DF 类来解决它,类似于 mock 的 ANY 类

class SAME_DF:
    def __init__(self, df: pd.DataFrame):
        self.df = df

    def __eq__(self, other):
        return isinstance(other, pd.DataFrame) and other.equals(self.df)
 
def test_called_with_df():
    ....
    mock.method.assert_called_once_with(SAME_DF(pd.DataFrame({
        'name': ['Eric', 'Yoav'],
        'age': [28, 34]
    })))
于 2021-09-01T08:23:47.600 回答