I am writing a unit test case for one of my python 2.7 methods.
In my test method, there is a method call that takes a dictionary with string key and panadas dataframe as the value for that key.
I want to write an interaction test for this method to check if it calls the method internally with correct dictionary
def MethodUnderTest(self):
#some code here
externalMethod(dictionary_of_string_dataframe)
#some code here
In the unit test, I do write my assert to test this interaction like this
mock_externalClass.externalMethod.assert_called_once_with(dictionary_of_string_dataframe)
I create dictionary_of_string_dataframe exactly the same way it is created in the actual method. In fact, I copied the helper method that does that in the test code just to make sure that both the dictionaries are the same. I even print both the dictionaries while debugging the test method on python console and both look exactly the same.
And I patch the external class using @patch decorator and all that works fine.
The problem is that in the above mentioned assert statement, i get the following error:
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().
I did search on the valueError but not much of help. Can someone tell me what's going on here ?
I did check the following question but that didn't help
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()
I also ran into this issue while trying to test that a function I wrote is called with a certain preprocessed dataframe, and I solved it using mock
's call_args attribute along with pandas
's testing.assert_frame_equal
.
In my case I wanted to assert the value of the dataframe passed as the second argument to my function below called score_function
that is called by the higher-level function run_scoring
. So first I retrieved the *args
part of the method call to the mock using [0]
and then I got my second positional argument (which is the one I wanted to assert the value of) using [1]
.
Then I could assert the value of this dataframe using 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]
})
)
This happens because unittest.mock compares between input values using ==
or !=
. However, pandas dataframes cannot be similarly compared, and instead, you must use the .equals
method of DataFrames.
https://github.com/testing-cabal/mock/blob/master/mock/mock.py
One possible fix is to write your own unit test that iterates through the dictionary and compares between dataframes using the .equals
method.
Another is to override the __equals__
method of pandas dataframes so when mock
compares between them it will use the correct method.
Here is how I would solve it based on the original question. This borrows some of the thinking in sfogle's answer, but it allows you to test called functions generally whether the args are DataFrames or not.
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With