- Mock
- 测试
【免费下载链接】moto
A library that allows you to easily mock out tests based on AWS infrastructure.
Moto 是一个以内存态方式模拟 AWS 基础设施的测试库,其workspaces-web模块为 Amazon WorkSpaces Web 服务提供了可离线运行的 API 实现,覆盖 Portal(门户)、Browser Settings(浏览器设置)、Network Settings(网络设置)、User Settings(用户设置)、User Access Logging Settings(用户访问日志设置)以及资源标签管理等核心能力。本文以官方服务文档 docs/docs/services/workspaces-web.rst 为主体,结合 moto/workspacesweb/models.py、moto/workspacesweb/responses.py 等源码与 tests/test_workspacesweb/test_workspacesweb.py 测试用例,系统讲解该模块支持哪些 API、数据模型如何组织、资源如何关联,以及如何用@mock_aws在单元测试中驱动 boto3 客户端完成完整的"创建-查询-关联-删除"闭环。
WorkSpaces Web 在 Moto 中的定位
Amazon WorkSpaces Web 是 AWS 提供的安全远程浏览器服务,管理员通过 Portal 向终端用户交付隔离的浏览器会话,并借助一系列 Settings 资源(网络、浏览器策略、用户权限、日志等)控制体验与安全边界。Moto 的workspaces-web模块把这一服务的能力搬进本地进程内存中,使 CI 与本地开发无需真实 AWS 账号即可验证集成代码。
从后端注册看,该服务通过 moto/backends.py 导入WorkSpacesWebBackend,并在 moto/backend_index.py 中以正则https?://workspaces-web\.(.+)\.amazonaws\.com挂接 URL 前缀;URL 路由本身定义在 moto/workspacesweb/urls.py,覆盖browserSettings、networkSettings、userSettings、userAccessLoggingSettings、portals、tags六类端点。也就是说,只要 boto3 客户端以region_name构造出workspaces-web.{region}.amazonaws.com的请求地址,Moto 的响应层即可接管并返回模拟数据。
API 覆盖清单:已实现与未实现
服务文档用勾选框形式逐条列出该服务下全部 API 的实现状态:[X]表示已实现,[ ]表示尚未实现(调用会返回未实现错误或不可用)。整体来看,已实现的 27 个 API覆盖了五大资源对象的创建、查询、列举、删除与关联,以及标签操作;未实现的 65 个 API集中在数据保护设置、IP 访问设置、身份提供商、会话日志、信任存储、会话管理及各类资源的 update 与 disassociate 操作。
已实现 API([X])
| 分类 | API |
|---|---|
| 关联操作 | associate_browser_settings、associate_network_settings、associate_user_access_logging_settings、associate_user_settings |
| 浏览器设置 | create_browser_settings、get_browser_settings、list_browser_settings、delete_browser_settings |
| 网络设置 | create_network_settings、get_network_settings、list_network_settings、delete_network_settings |
| 用户设置 | create_user_settings、get_user_settings、list_user_settings、delete_user_settings |
| 访问日志设置 | create_user_access_logging_settings、get_user_access_logging_settings、list_user_access_logging_settings、delete_user_access_logging_settings |
| 门户 | create_portal、get_portal、list_portals、delete_portal |
| 标签管理 | list_tags_for_resource、tag_resource、untag_resource |
未实现 API([ ])
其余操作当前均未在 Moto 中实现,包括但不限于:
- 设置类资源:
create/get/update/delete/associate/disassociate_data_protection_settings、ip_access_settings、session_logger、trust_store、identity_provider; - 更新与解绑:
update_browser_settings、update_network_settings、update_portal、update_user_settings、update_user_access_logging_settings,以及全部disassociate_*操作; - 会话与元数据:
expire_session、get_session、list_sessions、get_portal_service_provider_metadata、list_trust_store_certificates等。
编写测试或集成代码前,应先核对上表,避免对未实现的 API 抱有不切实际的预期;这是 Moto 服务文档的核心价值之一——它以机器可读的清单告诉你"哪些能用、哪些不能用"。
架构分层:URL → Response → Backend → Model
workspaces-web模块遵循 Moto 的标准四层架构,理解这条调用链有助于排查问题或扩展功能:
- URL 层(moto/workspacesweb/urls.py):定义 base URL 正则与路径模板,例如
{0}/portals/(?P<portalArn>[^/]+)portal/(?P<uuid>[^/]+)$、{0}/portals/(?P<portalArn>.*)/browserSettings$,将 HTTP 方法分派到 moto/workspacesweb/responses.py 中的静态处理器; - Response 层(
WorkSpacesWebResponse):继承BaseResponse,通过workspacesweb_backends[self.current_account][self.region]拿到当前账号 + 区域对应的后端实例,再对请求体做参数提取(self._get_param)与响应 JSON 序列化; - Backend 层(
WorkSpacesWebBackend):以SERVICE_NAMESPACE = "workspaces-web"标识,内部维护五个内存字典(network_settings、browser_settings、user_settings、user_access_logging_settings、portals),键均为资源 ARN,并持有TaggingService实例负责标签存储; - Model 层:
FakePortal、FakeBrowserSettings、FakeNetworkSettings、FakeUserSettings、FakeUserAccessLoggingSettings五个BaseModel子类,负责生成资源 ID、格式化 ARN 与序列化to_dict()。
后端通过BackendDict(WorkSpacesWebBackend, "workspaces-web")注册(见 moto/workspacesweb/models.py),因此每个 AWS 区域都有独立的状态空间——这与真实 AWS 的区域隔离语义一致,也是测试可以并行、互不污染的基础。
五大资源模型的字段与返回结构
1. Network Settings(网络设置)
由FakeNetworkSettings实现,创建时接受securityGroupIds(安全组 ID 列表)、subnetIds(子网 ID 列表)、vpcId(VPC ID),以及可选的tags。其 ARN 格式为:
arn:{partition}:workspaces-web:{region}:{account_id}:network-settings/{uuid}partition由 moto/utilities/utils.py 的get_partition(region_name)计算(如aws、aws-cn、aws-us-gov)。to_dict()返回networkSettingsArn、securityGroupIds、subnetIds、vpcId与associatedPortalArns(已关联门户的 ARN 列表)。
2. Browser Settings(浏览器设置)
FakeBrowserSettings接受additionalEncryptionContext(附加加密上下文)、browserPolicy(浏览器策略)、clientToken(幂等令牌)、customerManagedKey(客户管理 KMS 密钥)。浏览器设置同样维护associatedPortalArns,用于反向追踪哪些门户使用了该设置。
3. User Settings(用户设置)
FakeUserSettings是字段最丰富的模型,除加密上下文与密钥外,还包含一组权限开关与超时参数:
| 参数 | 说明 | 默认行为 |
|---|---|---|
copyAllowed/pasteAllowed/downloadAllowed/uploadAllowed/printAllowed/deepLinkAllowed | 各项用户操作是否允许 | 未传值时默认"Disabled" |
disconnectTimeoutInMinutes | 断连超时(分钟) | 透传原值 |
idleDisconnectTimeoutInMinutes | 空闲断连超时(分钟) | 透传原值 |
cookieSynchronizationConfiguration | Cookie 同步配置 | 透传原值 |
clientToken | 幂等令牌 | 透传原值 |
注意一个实现细节:Moto 将权限开关保存为字符串"Enabled"/"Disabled"(见 moto/workspacesweb/models.py),与真实 API 的布尔语义在存储层做了归一化,测试断言时应匹配字符串值——这也是 tests/test_workspacesweb/test_workspacesweb.py 中断言resp["copyAllowed"] == "Enabled"的原因。
4. User Access Logging Settings(用户访问日志设置)
FakeUserAccessLoggingSettings接受clientToken与kinesisStreamArn(Kinesis 数据流 ARN),用于把用户访问日志投递到 Kinesis。其 ARN 资源段为user-access-logging-settings/{uuid}。
5. Portal(门户)
FakePortal是核心资源,创建时接受additionalEncryptionContext、authenticationType(如"Standard")、clientToken、customerManagedKey、displayName、instanceType、maxConcurrentSessions。创建后 Moto 自动填充一系列派生字段:
portalEndpoint:形如{portal_id}.portal.aws;browserType:固定为"Chrome";rendererType:固定为"AppStream";status:固定为"CREATED",statusReason固定为"TestStatusReason";creationDate:使用datetime.datetime.now().isoformat()。
门户还持有七个可关联资源的 ARN 字段(browserSettingsArn、networkSettingsArn、trustStoreArn、ipAccessSettingsArn、userAccessLoggingSettingsArn、userSettingsArn),初始均为None。
关联机制:双向记账的 associate 操作
Moto 对associate_*的实现采用了"双向记账"模式:每次关联不仅把设置资源的 ARN 写入 Portal 的对应字段,还把 Portal ARN 追加到设置资源的associated_portal_arns列表中。以associate_browser_settings为例(moto/workspacesweb/models.py):
- 从
browser_settings与portals字典按 ARN 取出对象; browser_settings_object.associated_portal_arns.append(portal_arn);portal_object.browser_settings_arn = browser_settings_arn。
因此关联完成后,从任一侧查询都能得到完整关系:get_portal返回的browserSettingsArn已填充,get_browser_settings返回的associatedPortalArns则包含该门户。associate_network_settings、associate_user_settings、associate_user_access_logging_settings三个操作逻辑完全同构。测试 tests/test_workspacesweb/test_workspacesweb.py 正是同时断言了这两个方向的字段。
从 URL 层看,关联操作复用/portals/{portalArn}/xxx子路径(见 moto/workspacesweb/urls.py),响应层则从请求体读取设置资源 ARN、从 URL 路径中提取 portal ARN(self.parsed_url.path.split("/portals/")[-1].split("/browserSettings")[0])。
标签管理:TaggingService 与 Resource Groups 集成
标签能力由WorkSpacesWebBackend持有的TaggingService提供:
tag_resource(arn, tags):将dict[str, str]形式的标签写入服务;响应层先把[{Key, Value}]列表转换为字典(moto/workspacesweb/responses.py);untag_resource(arn, tag_keys):按键名删除标签;list_tags_for_resource(arn):返回[{Key, Value}]列表,供get/list校验;iter_tagged_resources():实现TaggableResourcesMixin,以resource_type="workspaces-web:portal"暴露门户资源,使resourcegroupstaggingapi服务的get_resources也能索引到这些门户(moto/workspacesweb/models.py)。
标签测试覆盖了五种资源类型的"添加 → 追加 → 删除"全流程(tests/test_workspacesweb/test_workspacesweb.py):初始标签、追加新标签后合并返回、按键删除后仅剩剩余标签。
实战:用 @mock_aws 跑通 WorkSpaces Web 完整链路
下面的示例综合了测试文件中的真实调用方式(boto3 客户端、@mock_aws装饰器),演示从零创建到关联、再到清理的完整闭环:
import boto3 from moto import mock_aws @mock_aws def test_workspaces_web_full_flow(): client = boto3.client("workspaces-web", region_name="eu-west-1") # 1. 创建网络设置(安全组/子网/VPC) net_arn = client.create_network_settings( securityGroupIds=["sg-0123456789abcdef0"], subnetIds=["subnet-0123456789abcdef0", "subnet-abcdef0123456789"], vpcId="vpc-0123456789abcdef0", tags=[{"Key": "Env", "Value": "test"}], )["networkSettingsArn"] # 2. 创建浏览器设置 browser_arn = client.create_browser_settings( additionalEncryptionContext={"Key1": "Value1"}, browserPolicy="TestBrowserPolicy", clientToken="TestClient", customerManagedKey="abcd1234-5678-90ab-cdef-FAKEKEY", )["browserSettingsArn"] # 3. 创建用户设置(注意权限开关是字符串) user_arn = client.create_user_settings( copyAllowed="Enabled", pasteAllowed="Disabled", printAllowed="Enabled", uploadAllowed="Disabled", downloadAllowed="Enabled", )["userSettingsArn"] # 4. 创建访问日志设置(指向 Kinesis 流) logging_arn = client.create_user_access_logging_settings( kinesisStreamArn="arn:aws:kinesis:eu-west-1:123456789012:stream/TestStream" )["userAccessLoggingSettingsArn"] # 5. 创建门户 portal_arn, portal_endpoint = ( client.create_portal( authenticationType="Standard", displayName="TestDisplayName", instanceType="TestInstanceType", maxConcurrentSessions=5, tags=[{"Key": "Env", "Value": "test"}], )["portalArn"], client.create_portal( authenticationType="Standard", displayName="TestDisplayName", instanceType="TestInstanceType", maxConcurrentSessions=5, tags=[{"Key": "Env", "Value": "test"}], )["portalEndpoint"], ) # 6. 建立双向关联 client.associate_network_settings(networkSettingsArn=net_arn, portalArn=portal_arn) client.associate_browser_settings(browserSettingsArn=browser_arn, portalArn=portal_arn) client.associate_user_settings(userSettingsArn=user_arn, portalArn=portal_arn) client.associate_user_access_logging_settings( userAccessLoggingSettingsArn=logging_arn, portalArn=portal_arn ) # 7. 验证关联:门户侧与设置侧都能看到对方 portal = client.get_portal(portalArn=portal_arn)["portal"] assert portal["networkSettingsArn"] == net_arn assert portal["browserSettingsArn"] == browser_arn assert portal["userSettingsArn"] == user_arn assert portal["userAccessLoggingSettingsArn"] == logging_arn browser = client.get_browser_settings(browserSettingsArn=browser_arn)["browserSettings"] assert browser["associatedPortalArns"] == [portal_arn] # 8. 标签:追加、合并、删除 client.tag_resource(resourceArn=portal_arn, tags=[{"Key": "Team", "Value": "core"}]) tags = client.list_tags_for_resource(resourceArn=portal_arn)["tags"] assert {"Key": "Env", "Value": "test"} in tags assert {"Key": "Team", "Value": "core"} in tags client.untag_resource(resourceArn=portal_arn, tagKeys=["Env"]) # 9. 清理:删除资源 client.delete_portal(portalArn=portal_arn) client.delete_network_settings(networkSettingsArn=net_arn) client.delete_browser_settings(browserSettingsArn=browser_arn) client.delete_user_settings(userSettingsArn=user_arn) client.delete_user_access_logging_settings(userAccessLoggingSettingsArn=logging_arn) assert client.list_portals()["portals"] == []要点说明:
@mock_aws会拦截 boto3 对该服务的全部请求,无需真实凭证;region_name决定后端实例与 ARN 中的区域段,测试中可使用eu-west-1、ap-southeast-1等任意区域;- ARN 遵循
arn:{partition}:workspaces-web:{region}:{account}:{resource}/{uuid}格式,uuid.uuid4()保证每次创建的资源 ID 唯一; - 权限开关传值必须是
"Enabled"/"Disabled"字符串; - 所有资源都以内存字典存储,测试结束即释放,无持久化副作用。
响应层细节:GET / DELETE 的路径解析
响应层的单资源 GET 与 DELETE 走同一路径模板,通过 HTTP 方法区分:GET调用get_*,其余方法(DELETE)调用delete_*(见 moto/workspacesweb/responses.py)。资源 ARN 从 URL 中按资源段提取:
network_settings_arn = unquote(self.parsed_url.path.split("/networkSettings/")[-1])例如请求GET /networkSettings/arn%3Aaws%3Aworkspaces-web%3Aeu-west-1%3A123456789012%3Anetwork-settings%2F{uuid}时,先对 URL 编码的 ARN 做unquote解码,再以/networkSettings/为界取末段。DELETE 成功后返回空 JSON{},GET 成功则返回{"networkSettings": {...}}之类的包裹结构。create_*请求的参数全部经self._get_param("xxx")从 JSON 请求体提取,参数名与 AWS API 的 camelCase 字段一一对应。
局限与后续演进方向
当前实现仍有明显边界,编写依赖这些能力的前置调研:
- 缺少 update 与 disassociate:
update_portal、update_browser_settings、update_network_settings、update_user_settings、update_user_access_logging_settings以及全部disassociate_*均未实现,关联一旦建立便不可在内存中解除; - 部分资源类型缺失:
data_protection_settings、ip_access_settings、session_logger、trust_store、identity_provider完全没有对应的模型与路由,涉及这些资源的用例目前无法模拟; - 门户字段为固定值:
browserType、rendererType、status、statusReason是写死的常量,portalEndpoint使用虚构域名*.portal.aws,不代表真实可用端点; - 会话与元数据接口缺席:
expire_session、get_session、list_sessions、get_portal_service_provider_metadata等会话级 API 不在实现清单中。
若需要这些能力,可关注 Moto 后续版本对workspaces-web覆盖率的扩展;当前版本建议把测试聚焦在本文"已实现 API"表格列出的 27 个操作上。
小结
Moto 的workspaces-web模块以四层架构(URL → Response → Backend → Model)完整模拟了 WorkSpaces Web 的五类核心资源与关联、标签能力,并给出 27 个可直接调用的 API。借助 docs/docs/services/workspaces-web.rst 的覆盖清单,开发者可以快速判断哪些操作可在本地测试中使用、哪些仍受限于实现边界。配合@mock_aws装饰器与 tests/test_workspacesweb/test_workspacesweb.py 中的既有用例,即可在 CI 中稳定验证门户创建、设置关联、标签管理与资源清理的完整业务逻辑。
- Mock
- 测试
【免费下载链接】moto
A library that allows you to easily mock out tests based on AWS infrastructure.
相关推荐
Moto 中 Amazon WorkSpaces 服务的模拟实现指南:已支持 API 全景与源码级剖析
Moto 中 Amazon WorkSpaces 服务的模拟实现指南:已支持 API 全景与源码级剖析 Amazon WorkSpaces 是 AWS 提供的托
Mock测试Moto CleanRooms 服务模拟指南:用 20 个已实现 API 在本地复现 AWS Clean Rooms
Moto CleanRooms 服务模拟指南:用 20 个已实现 API 在本地复现 AWS Clean Rooms 本篇技术指南聚焦 Moto 对 AWS C
Mock测试Moto AWS Support 服务模拟实战指南:在本地测试中模拟 AWS 工单与 Trusted Advisor 检查
Moto AWS Support 服务模拟实战指南:在本地测试中模拟 AWS 工单与 Trusted Advisor 检查 本篇技术指南围绕 Moto 项目中
Mock测试
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考