I'm trying to write test for this function. I'd like not to rely on real gcs object, but mock the objects.
# gcs_blobs.py file
from google.cloud import storage
def check_existing_blobs(new_blob_names: list[str], bucket: storage.Bucket) -> list[storage.Blob]:
# Check if any of the new blobs exists already on bucket
current_blobs = [blob for blob in bucket.list_blobs()]
existing_blobs = []
for blob_name in new_blob_names:
for blob in current_blobs:
if blob_name == blob.name:
existing_blobs.append(blob)
return existing_blobs
As I understand correctly, I should
- mock Bucket.list_blobs() method so that it would return list of mocked Blob objects
- mock Blob instance 'name' attribute so that it would return name I want
I'm trying to do that with no success. In test code below, check_existing_blobs(new_blob_names, bucket)
returns empty list. In this case I'd like it to return blob with name blob1.
How do I do that properly?
Would using 'side_effect' on blob.name allow me to return names from the provided 'existing_blob_names' list in consecutive calls to blob.name?
How to do this assertion properly, with list of blobs with names from the list?
# test_blobs.py file
from unittest.mock import Mock, MagicMock
import pytest
from google.cloud import storage
from .gcs_files import (
check_existing_blobs
)
@pytest.mark.parametrize(
"new_blob_names,existing_blob_names",
[
(
['blob1'],
['blob1', 'blob2'],
),
]
)
def test_contains_existing_files(mocker, new_blob_names, existing_blob_names):
storage.Bucket = MagicMock()
bucket = storage.Bucket()
storage.Blob = MagicMock()
blob = storage.Blob()
#mocker.patch.object(blob, "name", new="blob_name") # after that blob.name prints "blob_name"
mocker.patch.object(blob, "name", side_effect=existing_blob_names)
print(blob.name) # output: <MagicMock name='name' id='139862698975120'>
# in place of '???' it should be mock of storage.Blob instance which .name returns one of the blob names
# assert check_existing_blobs(new_blob_names, bucket) == [???]
Also please let me know if I should split this question to subproblems.
I'd recommend not using
MagicMock()
for this. Instead, just make a new class that has the behavior you want:I'd also recommend not assigning to
storage.Bucket
orstorage.Blob
. These assignments won't be reverted when the test function ends, so they have great potential to interfere with later tests. In fact, the purpose ofmocker.patch
andmocker.patch.object
is to avoid this kind of assignment. In your case, though, there's no reason to patch anything, because the code being tested doesn't rely on any global variables.