Skip to content

Mock 工具封装题

自测题

完成以下 3 道题目,检验你的学习成果

问题 1

Mock 规则应该覆盖哪些场景?

问题 2

为什么 Mock 服务需要清理机制?

问题 3

如何保证 Mock 响应与真实接口契约一致?

题目背景

理解面试官出这道题的意图

这道题考察的是测试隔离能力和工程化设计思维。在微服务架构和分布式系统中,外部依赖(第三方 API、下游服务、数据库等)常常不可控:可能不稳定、可能收费、可能尚未开发完成。Mock 工具的核心价值是隔离这些不可控依赖,让测试能够独立、稳定、可重复地执行。面试官想了解你是否能设计出一个灵活的 Mock 封装,支持多种场景、动态响应、规则匹配和状态管理,而不是简单地返回固定 JSON。工程化思维体现在能否将 Mock 抽象为可配置、可观测、可复用的服务模块。

解题思路

场景划分→响应控制→规则匹配→状态管理

  • 第一步:场景划分——明确 Mock 的使用场景。外部依赖不可用时,Mock 替代真实服务保证测试可执行。异常响应模拟时,构造超时、错误码、异常数据结构等场景验证系统容错能力。性能测试时,Mock 提供稳定响应消除外部干扰。特定业务状态模拟时,构造正常流程难以触发的状态(如支付失败、库存不足)。
  • 第二步:响应控制——Mock 不仅要返回正常响应,还要支持延迟模拟(模拟慢响应)、错误码返回(模拟服务端错误)、异常结构(模拟字段缺失或类型错误)、分页响应(模拟大数据量场景)。响应内容应支持模板变量,根据请求参数动态生成。
  • 第三步:规则匹配——Mock 规则要参数化,支持按请求路径、方法、参数、Header、Body 等条件匹配不同响应。匹配规则支持精确匹配、前缀匹配、正则匹配和自定义函数匹配。多条规则按优先级排序,高优先级规则先生效。
  • 第四步:状态管理——Mock 服务需要维护状态,支持场景切换(如从正常模式切换到故障模式)。状态要可查询(当前激活了哪些规则)、可清理(测试结束后重置状态),避免污染后续测试。支持调用次数统计,验证业务逻辑是否正确调用了依赖。
  • 设计权衡:Mock 粒度粗→维护简单但灵活性差。Mock 粒度细→灵活但维护成本高。静态 Mock→简单快速。动态 Mock→灵活但复杂度高。需要根据项目阶段和团队规模做选择。

代码逻辑

核心流程描述,不展示完整代码

【整体流程】Mock 服务启动 → 加载规则配置 → 监听请求 → 请求到达时匹配规则 → 找到匹配规则后生成响应 → 应用延迟配置 → 返回响应 → 记录调用日志。【核心步骤详解】1. 规则引擎:接收请求信息(路径、方法、参数、Body),遍历规则列表进行匹配。匹配策略支持精确匹配(URL + Method 完全一致)、模式匹配(正则表达式)、条件匹配(Body 中某字段等于特定值)。匹配到多条规则时按优先级选择。2. 响应生成器:根据匹配到的规则生成响应。支持静态响应(直接返回预设 JSON)、动态响应(根据请求参数计算返回值)、模板响应(使用模板引擎填充变量)。3. 延迟模拟器:根据规则配置的延迟时间,在返回响应前等待。支持固定延迟、随机延迟(模拟网络抖动)、渐进延迟(模拟服务降级)。4. 状态管理器:维护 Mock 服务的运行时状态。包括:已激活的规则列表、每条规则的调用次数、场景切换历史。支持 reset 操作清空所有状态。5. 调用记录器:记录每次 Mock 调用的详细信息,包括请求参数、匹配的规则、响应内容、耗时。用于验证业务逻辑是否正确调用了依赖,以及排查 Mock 相关问题。【关键接口定义】MockRule:包含匹配条件、响应模板、延迟配置、优先级、启用状态。MockContext:包含当前场景、已激活规则、调用统计。MockResult:包含响应内容、匹配的规则名、耗时。

示例代码:Mock 工具封装

# utils/mock_server.py - Mock 工具封装
import time
import json
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
class MockHandler(BaseHTTPRequestHandler):
"""Mock HTTP 处理器"""
rules: dict = {}
def do_GET(self):
self._handle("GET")
def do_POST(self):
self._handle("POST")
def _handle(self, method: str):
key = f"{method}:{self.path}"
rule = self.rules.get(key, {})
# 模拟延迟
delay = rule.get("delay", 0)
if delay:
time.sleep(delay)
self.send_response(rule.get("status_code", 200))
self.send_header("Content-Type", "application/json")
self.end_headers()
body = json.dumps(rule.get("body", {"error": "规则未找到"}), ensure_ascii=False)
self.wfile.write(body.encode("utf-8"))
def log_message(self, format, *args):
pass # 静默
class MockServer:
"""Mock 服务器"""
def __init__(self, port: int = 9999):
self.port = port
self.server = None
self.thread = None
def start(self):
self.server = HTTPServer(("localhost", self.port), MockHandler)
self.thread = threading.Thread(target=self.server.serve_forever)
self.thread.daemon = True
self.thread.start()
def add_rule(self, method: str, path: str, status_code: int = 200,
body: dict = None, delay: float = 0):
MockHandler.rules[f"{method}:{path}"] = {
"status_code": status_code,
"body": body or {},
"delay": delay,
}
def clear_rules(self):
MockHandler.rules.clear()
def stop(self):
if self.server:
self.server.shutdown()
self.clear_rules()
# 使用示例
import pytest
@pytest.fixture(scope="module")
def mock_server():
server = MockServer(port=9999)
server.start()
yield server
server.stop()
def test_payment_success(mock_server):
mock_server.add_rule(
method="POST", path="/pay",
status_code=200,
body={"status": "success", "transaction_id": "TXN123"},
)
import requests
resp = requests.post("http://localhost:9999/pay", json={"amount": 99.99})
assert resp.status_code == 200
assert resp.json()["status"] == "success"

常见失分点

面试中最容易丢分的 5 个问题

失分点 1:Mock 只模拟正常响应

错误做法:Mock 只返回 200 成功响应,不覆盖异常场景。

为什么不好:系统的容错能力无法得到验证。当真实服务返回异常时,系统可能崩溃或产生错误行为。测试覆盖率存在严重盲区。

如何改进:Mock 规则应覆盖:正常响应(200)、客户端错误(4xx)、服务端错误(5xx)、超时、连接拒绝、响应体字段缺失、数据类型错误等多种场景。

失分点 2:Mock 规则写死无法适配

错误做法:Mock 响应是硬编码的固定值,无法根据不同请求参数返回不同响应。

为什么不好:无法模拟业务的不同分支。例如根据用户 ID 返回不同权限的数据、根据订单状态返回不同的处理结果。测试用例的灵活性严重受限。

如何改进:Mock 规则支持参数化响应。使用模板变量(如 {{request.user_id}})或自定义函数,根据请求参数动态生成响应内容。

失分点 3:Mock 没有清理机制

错误做法:Mock 规则设置后一直生效,测试之间相互干扰。

为什么不好:前一个测试设置的 Mock 规则可能影响后一个测试的执行结果。例如测试 A 设置了支付失败的 Mock,测试 B 期望支付成功但实际被 Mock 拦截。

如何改进:每个测试用例结束后自动清理 Mock 规则。使用 Fixture 的 yield 机制,在测试前设置 Mock,测试后自动清理。或者提供显式的 reset/clear 方法。

失分点 4:Mock 与真实接口契约不一致

错误做法:Mock 响应的字段结构、数据类型与真实接口不一致。

为什么不好:基于 Mock 开发的功能在对接真实服务时可能出现问题。字段名拼写错误、类型不匹配、缺少必填字段等问题在联调时才会暴露。

如何改进:Mock 响应应基于 OpenAPI/Swagger 规范生成,保证与真实接口契约一致。定期同步真实接口的变更到 Mock 规则。使用契约测试工具(如 Pact)验证 Mock 与真实服务的一致性。

失分点 5:Mock 服务缺乏可观测性

错误做法:Mock 服务运行后无法知道被调用了多少次、哪些规则被触发、哪些规则从未被使用。

为什么不好:无法验证测试是否按预期执行。无法发现冗余的 Mock 规则。排查问题时无法确认是否是 Mock 配置导致的问题。

如何改进:Mock 服务提供调用统计接口:每条规则的调用次数、未匹配到的请求列表、平均响应时间。支持导出调用日志用于分析。

进阶讨论

展示技术深度和系统思维

【Mock 工具选型】自建 Mock vs 开源工具:自建 Mock 可控性强、贴合业务但开发成本高。开源工具(如 WireMock、MockServer、moco)功能完善但需要学习成本。选择依据:团队规模、项目复杂度、定制化需求。【契约测试集成】Mock 服务应与契约测试结合使用。消费者驱动契约(CDC):前端定义期望的接口格式,Mock 服务基于契约生成响应。提供者验证:后端服务验证是否符合契约定义。这样保证 Mock 与真实服务的一致性。【Mock 在 CI/CD 中的角色】Mock 服务在 CI 流水线中的价值:消除外部依赖,保证 CI 执行的稳定性和可重复性。加速测试执行,Mock 响应通常比真实服务快。支持并行测试,不同测试可使用不同的 Mock 配置。建议将 Mock 服务容器化,在 CI 中按需启动和销毁。

自测题

完成以下 3 道题目,检验你的学习成果

问题 1

Mock 规则应该覆盖哪些场景?

问题 2

为什么 Mock 服务需要清理机制?

问题 3

如何保证 Mock 响应与真实接口契约一致?