1

I am using Firestore with python to write Cloud Functions. I need to test a function with following query:

from google.cloud import firestore

MY_COLLECTION_GROUP = firestore.Client(project="my-project").collection_group("my_collection")

def function_to_test(data):
    my_query = (
        MY_COLLECTION_GROUP.where("eventDateTime", "==", data["dateTime"])
            .where("user.id", "==", data["user_id"])
            .where("user.age", ">=", data["valid_age"])
    )
    # fetch results from query and proceed further

To test the above function, I tried to mock MY_COLLECTION_GROUP's where function to test the queries. Like this:

from mock import call, patch

@patch("mymodule.MY_COLLECTION_GROUP.where")
def test_function_to_test(mocked_where_function):
    # called the function with some data
    function_to_test(data)

    # tried asserting the mock calls like this
    mocked_where_function.assert_has_calls([
        call("eventDateTime", "==", data["dateTime"]),
        call("user.id", "==", data["user_id"]),
        call("user.age", ">=", data["valid_age"]),
    ])

But it seems like it was only mocking the first where function and not all, I got the following error while running test:

E               AssertionError: Calls not found.
E               Expected: [call('eventDateTime', '==', '2021, 09, 15'),
E                call('user.id', '==', '12'),
E                call('user.age', '>=', 18)]
E               Actual: [call('eventDateTime', '==', '2021, 09, 15'),
E                call().where('user.id', '==', '12'),
E                call().where().where('user.age', '>=', 18))]

So, is there any way to mock all where functions and not just the first one?

What am I doing wrong here?

Gourav B
  • 864
  • 5
  • 17
Keshav Bhatiya
  • 295
  • 3
  • 11

1 Answers1

0

Looking at the differences between the expected and actual calls, it seems that the subsequent “where” is called on the return value of the previous call. Based on the python documentation, when using assert_has_calls, mock_calls list is checked for the calls. And mock_calls records all calls to the mock object, its methods, magic methods and return value mocks. Calls on the returned object are also in the mock_calls list. So you should set the expected to the ones they observe in the actual calls, as only the first “where” is called on the mock object, subsequent calls are on the returned value of the mock object.

Your issue is similar to the questions asked in this other post where they mentioned that the issue could be solved using any_order=True to your assert_has_calls call, like this:

m.run.assert_has_calls([ 
mock.call(['foo']), 
mock.call(['bar']), 
], any_order=True)
Priyashree Bhadra
  • 3,182
  • 6
  • 23