0

Python 3.7. I'm trying to unit test the function below:

    def is_json_clean():
        # function for is_json_clean here, returns bool

    def json_function(blob, raw_bucket, lake_bucket):
        data_string = blob.download_as_bytes()
        data = json.loads(data_string)
        if is_json_clean(data) is True:
            raw_bucket.copy_blob(blob, lake_bucket)

I'm pretty new to mock patches, and could use an example here! How could I use mock.patch or something similar to get that if statement with my is_json_clean() function to return True so that I can assert that the copy_blob function was called? I thought I could make a patch like:

@mock.patch.object(imported_module, "is_json_clean", return_value=True)
def test_json_function(mock):
    # rest of the test function 
    # including assert statement for the copy_blob function 

but how then would I insert this into my test to assert my copy_blob function? I know that quite a few unit test-related questions are out there, but I haven't found exactly the answer to my question yet (like I said, I'm pretty new). Any help is appreciated. Thank you!

  • you need to add the above decorator patch in the test method, you can refer to examples in [mock-patch-object-to-change-the-return-value-of-a-method-called](https://stackoverflow.com/questions/18191275/using-pythons-mock-patch-object-to-change-the-return-value-of-a-method-called-w) . – Krishna Chaurasia Mar 12 '21 at 04:22
  • @KrishnaChaurasia Okay, correction: I did add this patch to a test method! Should have clarified that. What I'm asking now is how I would then reference that patch in the test function itself to force the if statement to be TRUE and then let copy_blob() be called. – idly-and-sambal Mar 12 '21 at 04:25
  • you don't need to do anything, just call the method to be tested and in the test path, the mocked method will replace the original implementation. you can add a `print()` statement in the original method in the `if` path and you should see the same while executing the test. – Krishna Chaurasia Mar 12 '21 at 04:27
  • I see! What you're saying makes sense! Is there any reason then that the function copy_blob wouldn't be called when I run the test? That's my current issue. – idly-and-sambal Mar 12 '21 at 04:29
  • I am not sure what is happening there, you could just add a `print()` in the `if` path to see if the patch is working as expected and then dig deeper by using a debugger. – Krishna Chaurasia Mar 12 '21 at 04:34
  • `is True` is almost always a bad idea, by the way - it's longer and slower than just leaving it out, and it's sometimes outright wrong. The only time you should ever use `is True` is if you need to distinguish `True` from objects of other types, which isn't the case here. – user2357112 Mar 12 '21 at 20:42

1 Answers1

0

Your mocking looks correct, but I don't see the blob, raw_bucket, or lake_bucket args being passed in within your test case. Here's how I would write it, mocking all 3 of those args.

import unittest

from unittest import mock

import imported_module

class MyTest(unittest.TestCase):
    @mock.patch.object(imported_module, "is_json_clean", return_value=True)
    def test_json_function(self, mock_is_json_clean):
        json_blob = '{"mykey": "myvalue"}'
        mock_blob = mock.Mock()
        mock_blob.download_as_bytes.return_value = json_blob
        mock_raw_bucket = mock.Mock()
        mock_lake_bucket = mock.Mock()

        imported_module.json_function(mock_blob, mock_raw_bucket, mock_lake_bucket)

        mock_raw_bucket.copy_blob.assert_called_once_with(mock_blob, mock_lake_bucket)

if __name__ == "__main__":
    unittest.main()

You can see that I pass in a mock_blob and mock the return value of download_as_bytes, as well as passing in the two buckets. From there, I'm able to do an assert on the mock_raw_bucket.copy_blob call. I was able to get this code running and passing here: https://replit.com/@WesH1/TautLiveTelephone#main.py

wholevinski
  • 3,658
  • 17
  • 23