- Mock
- 测试
【免费下载链接】moto
A library that allows you to easily mock out tests based on AWS infrastructure.
本指南以 docs/docs/services/rekognition.rst 为骨架,系统梳理 moto 对 AWS Rekognition 服务的模拟实现:已支持的 8 个 API、各接口的返回结构、底层源码实现位置与测试验证方式。读完本文,你将能在本地测试中正确使用 Rekognition 的 mock 能力,并清楚哪些接口返回的是硬编码数据、哪些接口尚未实现。
Rekognition 在 moto 中的实现位置与整体架构
Rekognition 是 AWS 的图像与视频分析服务(人脸比对、标签识别、文本检测等)。moto 以标准的"路由层 + 响应层 + 后端模型层"三段式结构实现该服务,对应源码分布在 4 个文件中:
| 文件 | 职责 |
|---|---|
| moto/rekognition/urls.py | 定义服务 URL 匹配规则,将请求路由到响应处理类 |
| moto/rekognition/responses.py | RekognitionResponse类,负责解析请求并序列化 JSON 响应 |
| moto/rekognition/models.py | RekognitionBackend类,存储模拟状态并生成各接口返回值 |
| moto/rekognition/init.py | 导出后端注册表,供 moto 全局发现 |
在 moto/rekognition/urls.py 中,服务端点通过正则https?://rekognition\.(.+)\.amazonaws\.com匹配,所有请求统一走{0}/$路由交给RekognitionResponse.dispatch分发;同时 moto/backend_index.py 也注册了同一正则,确保 moto 能识别 Rekognition 区域端点。
后端实例由 moto/rekognition/models.py 末尾的rekognition_backends = BackendDict(RekognitionBackend, "rekognition")按账号与区域管理,与 moto 其他服务一致,每个 account/region 组合都有独立的模拟实例。
支持范围:10% 覆盖率与 8 个已实现操作
原文档(也是 IMPLEMENTATION_COVERAGE.md 的生成依据)通过勾选清单明确了 Rekognition 的实现进度:约 10% 的 API 已实现,共 8 个,其余 50+ 个操作未实现。已实现列表如下:
| 已实现操作 | 说明 | 参数是否生效 |
|---|---|---|
compare_faces | 比对两张图片中的人脸相似度 | 参数不被处理,返回固定数据 |
detect_custom_labels | 调用自定义模型检测标签 | 参数不被处理,返回固定数据 |
detect_labels | 检测图片中的物体标签 | 参数不被处理,返回固定数据 |
detect_text | 检测图片中的文本 | 参数不被处理,返回固定数据 |
get_face_search | 获取人脸搜索任务结果 | 返回硬编码值,参数不生效 |
get_text_detection | 获取文本检测任务结果 | 返回硬编码值,参数不生效 |
start_face_search | 启动人脸搜索异步任务 | 生成任务 ID(JobId) |
start_text_detection | 启动文本检测异步任务 | 生成任务 ID(JobId) |
原文档对get_face_search与get_text_detection特别标注了 "This returns hardcoded values and none of the parameters are taken into account."(返回硬编码值且所有参数都不被处理),这一点在源码 moto/rekognition/models.py 的 docstring 中完全一致,属于项目明确声明的行为。
核心操作详解:调用链与返回结构
1. 异步任务启动:start_face_search 与 start_text_detection
这两个操作模拟 AWS 的异步视频分析流程:调用后返回一个 64 位任务 ID,后续通过get_face_search/get_text_detection查询结果。
调用链为:RekognitionResponse.start_face_search(moto/rekognition/responses.py)→RekognitionBackend.start_face_search(moto/rekognition/models.py)。后端通过_job_id()生成任务 ID,实现如下:
def _job_id(self) -> str: return "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(64) )即从大写字母与数字中随机抽取 64 个字符组成 JobId(随机源为moto.moto_api._internal.mock_random,在测试中可被set_initial_no_auth_action_exception等机制控制)。响应层返回 HTTP 200,并设置Content-Type: application/x-amz-json-1.1头,响应体为{"JobId":"..."}。
注意:该接口的CollectionId、Video(S3 对象信息)等入参在源码中并未被读取或存储,仅用于满足 boto3 客户端调用时的参数校验。
2. 查询任务结果:get_face_search 与 get_text_detection
这两个接口返回完全硬编码的结果,且不校验 JobId 是否存在——传入任意 JobId 都会得到相同响应。返回结构包含 6 个字段:
JobStatus:固定为"SUCCEEDED"(_job_status());StatusMessage:固定为空字符串;VideoMetadata:固定视频元数据(见下文);Persons/TextDetections:固定的人物/文本检测数组;NextToken:固定为空字符串(_next_token());TextModelVersion:固定为"3.1"(_text_model_version())。
固定的VideoMetadata(moto/rekognition/models.py)为:
{ "Codec": "h264", "DurationMillis": 15020, "Format": "QuickTime / MOV", "FrameRate": 24.0, "FrameHeight": 720, "FrameWidth": 1280, "ColorRange": "LIMITED", }get_face_search的Persons中,FaceMatches引用了ExternalImageId: "Dave_Bloggs"的人脸,包含BoundingBox、Landmarks(eyeLeft/eyeRight/mouthLeft/mouthRight/nose 五点)、Pose(Roll/Yaw/Pitch)、Quality(Brightness/Sharpness)与高达 99.99 的Confidence与Similarity值。get_text_detection的TextDetections则包含 "Hello world" / "Hello" / "world" / "Goodbye world" 等文本,按LINE(行)与WORD(词)类型组织,WORD通过ParentId关联到所属行,时间戳分布在 0ms 与 1000ms。
3. 图片分析:compare_faces、detect_labels、detect_text、detect_custom_labels
这四个同步接口同样返回硬编码数据,所有入参(Image、MaxLabels、SimilarityThreshold、ProjectVersionArn、MinConfidence等)均不参与计算:
- compare_faces:返回
FaceMatches(固定 1 条,Similarity: 100.0的完整人脸结构)、SourceImageOrientationCorrection与TargetImageOrientationCorrection(固定为"ROTATE_90")、UnmatchedFaces(固定 1 条未匹配人脸)以及SourceImageFace。定义见 moto/rekognition/models.py。 - detect_labels:返回
Labels(固定 "Mobile Phone" 标签,含Parents: [{"Name": "Phone"}]、Aliases: [{"Name": "Cell Phone"}]、Categories: [{"Name": "Technology and Computing"}]、Instances中的 BoundingBox 与 DominantColors)、ImageProperties(Quality、DominantColors、Foreground、Background 三部分)以及LabelModelVersion: "3.0"。 - detect_text:返回
TextDetections("IT'S MONDAY but keep Smiling" 的 LINE 与 WORD 结构,含Id、ParentId、Geometry的 BoundingBox 与 Polygon)和TextModelVersion: "3.0"。 - detect_custom_labels:返回
CustomLabels(固定 1 条:Name: "MyLogo",Confidence: 77.77,含 BoundingBox)。
这些方法在 moto/rekognition/models.py 中均为无参或忽略参数的方法,直接返回_face_matches()、_mobile_phone_label()、_detect_text_text_detections()、_detect_custom_labels_detections()等私有方法生成的固定数据,由 moto/rekognition/responses.py 包装成标准 JSON 响应。
测试验证:测试用例与可断言的固定值
已实现操作的预期行为全部由 tests/test_rekognition/test_rekognition.py 覆盖,可作为"返回内容是否如预期"的直接依据:
| 测试函数 | 关键断言 |
|---|---|
test_start_face_search | HTTP 200,响应含JobId |
test_start_text_detection | HTTP 200,响应含JobId |
test_compare_faces | 响应含FaceMatches |
test_detect_labels | 响应含Labels |
test_detect_text | 响应含TextDetections |
test_get_face_search | JobStatus == "SUCCEEDED",且Persons[0]["FaceMatches"][0]["Face"]["ExternalImageId"] == "Dave_Bloggs" |
test_get_text_detection | TextDetections[0]["TextDetection"]["DetectedText"] == "Hello world" |
test_detect_custom_labels | CustomLabels[0]["Name"] == "MyLogo" |
从测试代码可以看到,mock 使用统一采用@mock_aws装饰器 +boto3.client("rekognition", region_name=...)的方式,区域使用如ap-southeast-1、us-east-2等均可正常响应。
实际使用示例
以下完整示例展示了如何用 moto 在本地测试中驱动 Rekognition 的已支持接口:
import boto3 from moto import mock_aws @mock_aws def test_rekognition_mock(): client = boto3.client("rekognition", region_name="ap-southeast-1") # 1. 启动异步任务,拿到 JobId resp = client.start_face_search( CollectionId="my-collection", Video={"S3Object": {"Bucket": "bucket", "Name": "video.mp4"}}, ) job_id = resp["JobId"] assert isinstance(job_id, str) and len(job_id) == 64 # 2. 查询任务结果(硬编码:SUCCEEDED + Dave_Bloggs) result = client.get_face_search(JobId=job_id) assert result["JobStatus"] == "SUCCEEDED" assert ( result["Persons"][0]["FaceMatches"][0]["Face"]["ExternalImageId"] == "Dave_Bloggs" ) # 3. 图片同步分析(均返回固定数据) image = {"S3Object": {"Bucket": "bucket", "Name": "photo.jpg"}} assert "Labels" in client.detect_labels(Image=image, MaxLabels=10) assert "TextDetections" in client.detect_text(Image=image) assert "FaceMatches" in client.compare_faces( SimilarityThreshold=80, SourceImage=image, TargetImage=image, ) assert client.detect_custom_labels( Image=image, ProjectVersionArn="arn:aws:rekognition:us-east-2:123456789012:project/logo/version/v1", MinConfidence=80, )["CustomLabels"][0]["Name"] == "MyLogo"要点提示:
- 所有已实现接口都不依赖真实的 S3 文件内容,
Video/Image参数仅需满足 boto3 的结构校验(S3Object的Bucket/Name即可); - 对于
start_face_search、start_text_detection之外的接口,传入任何参数值都不会影响返回结果; - 若需在真实 AWS 上运行,请将
@mock_aws装饰器移除并配置真实凭证,但此时返回结果将是真实模型推断,与本文描述的行为完全不同。
未实现操作与使用边界
原文档清单中,以下 50+ 个操作均未实现(调用会抛出异常或不受支持),包括但不限于:create_collection/delete_collection/describe_collection、index_faces、search_faces、search_faces_by_image、detect_faces、recognize_celebrities、list_collections、list_faces、create_project与create_project_version等项目生命周期管理接口,以及start_label_detection、start_content_moderation、start_person_tracking、get_label_detection、get_content_moderation等视频分析接口,还有tag_resource/untag_resource/list_tags_for_resource等资源标签操作。
因此在编写依赖 Rekognition 的测试时需注意:
- 集合(Collection)类操作不可用:无法通过 mock 创建集合或把人脸写入集合,
get_face_search返回的FaceMatches是硬编码的 "Dave_Bloggs" 数据,与任何实际集合状态无关; - 状态管理缺失:
get_face_search/get_text_detection不校验 JobId,也不维护任务状态机,同一请求无论调用多少次、传什么 JobId,结果都恒定; - 若你的业务测试强依赖未实现接口,可考虑使用 moto 的
moto_api相关能力或自行在测试层打桩(stub),具体扩展方式可参考 moto 其他服务的实现模式(例如同样位于 moto/backend_index.py 中注册的其他服务)。
小结
moto 对 Rekognition 的支持定位是"最小可用模拟":8 个已实现接口能覆盖"启动异步任务 → 查询硬编码结果 → 同步图片分析"这条基本调用链路,足以支撑不校验具体识别内容的单元测试与集成测试。但其数据完全硬编码、参数不生效、覆盖率仅 10% 的特性决定了它更适合作为"流程性验证"而非"内容验证"工具——在断言具体识别结果前,请先以 tests/test_rekognition/test_rekognition.py 中的固定值为准核对预期。
- Mock
- 测试
【免费下载链接】moto
A library that allows you to easily mock out tests based on AWS infrastructure.
相关推荐
moto 中 AWS DevOps Agent(devops-agent)服务的 Mock 支持:已实现操作、使用方式与源码实现解析
moto 中 AWS DevOps Agent(devops agent)服务的 Mock 支持:已实现操作、使用方式与源码实现解析 本篇以 moto 仓库中
Mock测试moto 中 AWS Config 服务的实现全解:已支持 API、Recorder/Aggregator 机制与源码级剖析
moto 中 AWS Config 服务的实现全解:已支持 API、Recorder/Aggregator 机制与源码级剖析 本文以 docs/docs/ser
Mock测试Moto 中的 AWS App Mesh 模拟实现:AppMeshBackend 已支持 API 全解析与实战指南
Moto 中的 AWS App Mesh 模拟实现:AppMeshBackend 已支持 API 全解析与实战指南 本文聚焦开源库 Moto 对 AWS App
Mock测试
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考