我正在尝试使用 Pytest、Moto (4.1.6) 和 s3fs (0.4.2) 为与 S3 交互的函数实现单元测试。
到目前为止,我能够创建一个存储桶并用 data
中的所有文件填充它。文件夹。
不幸的是,我的要求之一是我需要使用s3fs.core.S3FileSystem object
访问存储桶。类,因为这就是我们内部库的工作方式,我试图尽可能接近原始环境。
如果我在尝试访问假存储桶时没有遇到访问被拒绝的情况,那就不成问题了。
这是来自 conftest.py
的相关代码
#!/usr/bin/env python3
from moto import mock_s3
from pathlib import Path
import boto3
import os
import pytest
import s3fs
@pytest.fixture(scope="session")
def test_data_folder():
return os.path.join(os.path.dirname(__file__), "data")
@pytest.fixture(scope="session")
@mock_s3
def s3_filesystem(test_data_folder):
connection = boto3.client("s3", region_name="us-east-1")
connection.create_bucket(Bucket="bucket")
for path in Path(test_data_folder).rglob("*"):
if path.is_file():
with open(path, "rb") as parquet:
data = parquet.read()
connection.put_object(Bucket="bucket", Key=str(path), Body=data)
bucket = boto3.resource("s3").Bucket("bucket")
for object in bucket.objects.all():
print(object.key)
filesystem = s3fs.S3FileSystem(anon=True)
filesystem.ls(test_data_folder)
return filesystem
运行此代码后,我可以在打印输出中看到其中存在几个如下所示的文件:
/Users/campos/repos/project/tests/data/20221027/transactions_test_20221027.parquet
我想返回s3fs.core.S3FileSystem object
根据我的测试,但是当我尝试运行 filesystem.ls(test_data_folder)
时在我的调试器中我得到 *** PermissionError: All access to this object has been disabled
再深入一点,从 objects.bucket.all()
返回的对象看起来像这样:
s3.ObjectSummary(bucket_name='bucket', key='/Users/campos/repos/project/tests/data/20221027/orders_test_20221027.parquet')
我已经尝试将公共(public)访问控制列表添加到存储桶创建中,如下所示 s3.create_bucket(Bucket="bucket", ACL="public-read")
但它并没有改变任何东西。
我还在错误消息中看到了这一点:
api_params = {'Bucket': 'Users', 'Delimiter': '/', 'EncodingType': 'url', 'Prefix': 'ykb595/repos/gfctr-card-lob-pyexetl/tests/data/'}
...
botocore.errorfactory.NoSuchBucket: An error occurred (NoSuchBucket) when calling the ListObjectsV2 operation: The specified bucket does not exist
很明显,我的文件存在于某种存储桶中的某个位置,但看起来好像无法找到该存储桶。
我错过了什么?
提前谢谢您!!
最佳答案
将此 filesystem.ls(test_data_folder)
更改为 filesystem.ls("bucket")
test_data_folder
解析为 home/username/..
(对我来说),这意味着 S3FS 尝试查找名为 home
的存储桶。但是您在其他地方使用的存储桶名称是bucket
。
目前,S3/Moto 中创建的存储桶结构如下所示:
/
-> home/
-> folder1/
-> folder2/
-> 等等
我认为 S3FS 不太喜欢这个,无论是结构本身还是第一个文件夹名为 /
的事实。当删除 mock_s3
装饰器并针对 AWS 本身进行测试时,对 filesystem.ls(bucket_name)
的调用严重失败,并且 filesystem.walk()
返回相同的奇怪结果。 ([('桶', [], [''])]
)
当我在 S3 中创建平面结构时,filesystem.walk()
确实按预期工作(针对 AWS 和 Moto):
# Create files using only the last section of the path:
with open(path, "rb") as parquet:
data = parquet.read()
connection.put_object(Bucket=bucket_name, Key=path.parts[-1], Body=data)
结果:
[('/bucket', [], ['file1.py', 'file2.py', 'file3.py'])]
一般来说,您要求按照最佳实践更好地理解、研究和实现 AWS 测试
。
如果您不确定预期结果,我的建议始终是首先针对 AWS 验证行为。 S3FS 和 Moto 等开源工具非常适合编写单元测试,并验证已知行为不会改变。 但如果您不知道预期的结果,您就永远无法确定您所看到的奇怪的事情是 S3FS、Moto 还是 AWS 的结果。
关于pytest - 如何使用 S3FileSystem、Pytest 和 Moto 访问我自己的假存储桶,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75902766/