如何结合assertDictEqual与pandas.testing.assert_frame_equal进行测试?
解决方案:结合
assertDictEqual与pandas.testing.assert_frame_equal对比嵌套字典中的DataFrame 问题核心在于:Python标准库的assertDictEqual通过对象身份(内存地址)判断相等性,而Pandas DataFrame需要按内容(数据、结构、类型等)比较,直接使用assertDictEqual会因两个DataFrame是不同对象而断言失败。以下是几种经典解决方法:
方法1:递归遍历字典,针对性断言
编写辅助函数递归遍历嵌套字典每一层:遇到DataFrame就用pandas.testing.assert_frame_equal做内容比对,遇到子字典就递归处理,其他类型用普通相等性检查。
import pandas as pd import unittest def function_to_test(): data = {'Name': ['Tom', 'Joseph'], 'Age': [20, 21]} df = pd.DataFrame(data) return {"key_first_level": {"key_second_level": df}} def assert_dict_with_frames_equal(expected, actual): # 先确认键集合完全一致 assert set(expected.keys()) == set(actual.keys()), "字典键集合不匹配" for key in expected: val_exp = expected[key] val_act = actual[key] # 处理DataFrame类型 if isinstance(val_exp, pd.DataFrame) and isinstance(val_act, pd.DataFrame): pd.testing.assert_frame_equal(val_exp, val_act) # 可选:不需要严格检查dtype时,添加check_dtype=False参数 # 处理嵌套字典 elif isinstance(val_exp, dict) and isinstance(val_act, dict): assert_dict_with_frames_equal(val_exp, val_act) # 其他类型直接比较值 else: assert val_exp == val_act, f"键[{key}]的值不匹配:{val_exp} vs {val_act}" class TestFunction(unittest.TestCase): def test_function_to_test(self): data = {'Name': ['Tom', 'Joseph'], 'Age': [20, 21]} df = pd.DataFrame(data) expected_dict = {"key_first_level": {"key_second_level": df}} actual_dict = function_to_test() # 调用自定义断言函数 assert_dict_with_frames_equal(expected_dict, actual_dict) if __name__ == '__main__': unittest.main()
优点:
- 灵活适配任意深度的嵌套字典
- 可轻松扩展支持其他Pandas对象(如
pd.Series,只需添加对应类型判断) - 逻辑清晰,便于调试
方法2:扩展unittest的断言逻辑
通过unittest.TestCase.addTypeEqualityFunc方法,给dict类型注册自定义比较函数,这样可直接用self.assertDictEqual,它会自动处理字典中的DataFrame。
import pandas as pd import unittest def function_to_test(): data = {'Name': ['Tom', 'Joseph'], 'Age': [20, 21]} df = pd.DataFrame(data) return {"key_first_level": {"key_second_level": df}} def compare_dict_with_frames(test_case, expected, actual, msg=None): # 先检查键集合 test_case.assertEqual(set(expected.keys()), set(actual.keys()), msg=msg) for key in expected: exp_val = expected[key] act_val = actual[key] if isinstance(exp_val, pd.DataFrame) and isinstance(act_val, pd.DataFrame): pd.testing.assert_frame_equal(exp_val, act_val, msg=msg) elif isinstance(exp_val, dict) and isinstance(act_val, dict): compare_dict_with_frames(test_case, exp_val, act_val, msg=msg) else: test_case.assertEqual(exp_val, act_val, msg=f"键[{key}]不匹配") class TestFunction(unittest.TestCase): def setUp(self): # 为dict类型注册自定义比较逻辑 self.addTypeEqualityFunc(dict, compare_dict_with_frames) def test_function_to_test(self): data = {'Name': ['Tom', 'Joseph'], 'Age': [20, 21]} df = pd.DataFrame(data) expected_dict = {"key_first_level": {"key_second_level": df}} actual_dict = function_to_test() # 直接用标准的assertDictEqual即可 self.assertDictEqual(expected_dict, actual_dict) if __name__ == '__main__': unittest.main()
优点:
- 符合unittest原生风格,无需修改原有测试代码的断言调用
- 一次注册,整个测试类的所有
assertDictEqual都能自动处理DataFrame
方法3:转换DataFrame为字典(仅适用于简单场景)
若字典中的DataFrame结构简单(无datetime、categorical等复杂类型),可将预期和实际的DataFrame都转换成字典,再用assertDictEqual比较。
import pandas as pd import unittest def function_to_test(): data = {'Name': ['Tom', 'Joseph'], 'Age': [20, 21]} df = pd.DataFrame(data) return {"key_first_level": {"key_second_level": df}} class TestFunction(unittest.TestCase): def test_function_to_test(self): data = {'Name': ['Tom', 'Joseph'], 'Age': [20, 21]} df = pd.DataFrame(data) # 预期字典中的DataFrame转成dict expected_dict = {"key_first_level": {"key_second_level": df.to_dict()}} actual_dict = function_to_test() # 实际返回的字典中的DataFrame也转成dict actual_converted = { k: {sub_k: sub_v.to_dict() for sub_k, sub_v in v.items()} for k, v in actual_dict.items() } self.assertDictEqual(expected_dict, actual_converted) if __name__ == '__main__': unittest.main()
缺点:
- 复杂类型转换后可能出现不一致(如datetime会变成字符串或时间戳)
- 嵌套层级深时,转换代码会变得繁琐
内容的提问来源于stack exchange,提问作者amit
相关产品推荐
相关产品推荐

