我正在尝试为此函数编写测试。我不想依赖真正的 gcs 对象,而是模拟对象。
# 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
据我正确理解,我应该
我正在尝试这样做,但没有成功。在下面的测试代码中,
check_existing_blobs(new_blob_names, bucket)
返回空列表。在这种情况下,我希望它返回名为 blob1 的 blob。
我该如何正确地做到这一点?
在 blob.name 上使用“side_effect”是否允许我在连续调用 blob.name 时从提供的“existing_blob_names”列表中返回名称?
如何使用具有列表中名称的 blob 列表正确执行此断言?
# 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) == [???]
还请告诉我是否应该将此问题拆分为子问题。
我建议不要为此使用
MagicMock()
。相反,只需创建一个具有您想要的行为的新类即可:
class MockBucket:
def __init__(self, names):
self.blobs = [MockBlob(x) for x in names]
def list_blobs(self):
return self.blobs
class MockBlob:
def __init__(self, name):
self.name = name
我还建议不要分配给
storage.Bucket
或 storage.Blob
。当测试功能结束时,这些分配不会被恢复,因此它们很有可能干扰以后的测试。事实上,mocker.patch
和mocker.patch.object
的目的就是为了避免这种赋值。但就您而言,没有理由修补任何内容,因为正在测试的代码不依赖于任何全局变量。