面试基础问答模拟 — NCS QA Engineer Intern
说明:基于面试官背调 + JD 要求 + 候选人项目经验,模拟 19 个最可能被问到的技术/流程问题,给出回答模板和策略。 适用:NCS QA Engineer Intern 面试
目录
- Python 在自动化中有什么优势?为什么这么多人选择它?
- 你怎么用 Python 实现视频测试自动化?
- Python 怎么配置 API 来做到自动化交互?
- 为什么要测试视频?视频都是从哪来的?
- 你知道怎么接摄像头吗?
- 你怎么用 SQL 来管理测试的数据?
- 你知道测试的准则是什么吗?
- FastAPI 跟 RESTful API 的区别在哪?
- 你知道怎么找出 Bug 吗?找出后你要怎么处理?
- 你对自动化的流程怎么理解?具体会怎么实施?
- 如果要优化自动化,你有什么提议?
- 你怎么用 Bash/PowerShell 脚本实现自动化?
- 你知道怎么运行脚本吗?
- 你对 Linux 了解多少?
- 你对 Docker, Kubernetes 了解多少?
- 你是怎么配置后端与数据库交互的?
- 你对 pytest,软件测试了解多少?
- 你对数据库 ORM 有什么了解?它担任什么角色?
1. Python 在自动化中有什么优势?为什么这么多人选择它?
面试官想听什么
| 考察点 | 具体内容 |
|---|---|
| ✅ 你真的用过 Python,不是只会 Hello World | 能举出具体例子 |
| ✅ 你理解”为什么选 Python”而不是其他语言 | 有比较思维 |
| ✅ 你能说出 Python 做自动化的具体场景 | 跟 QA 岗位相关 |
回答模板
“从我个人的使用经验来看,Python 在自动化方面有 几个核心优势:
1. 语法简洁,上手快 同样的自动化脚本,Python 代码量大概是 Java 的 1/3 到 1/2。写一个 API 测试,
requests.get()一行就搞定了。2. 生态强大,库丰富
requests/httpx— HTTP 请求pytest— 测试框架selenium/playwright— 浏览器自动化opencv-python/ffmpeg-python— 视频处理paramiko— SSH 远程操作schedule— 定时任务基本上你想做的自动化,都有现成的库。
3. 跨平台 同一个 Python 脚本,Windows/Linux/macOS 都能跑。不像 Bash 脚本只能在 Linux 用,PowerShell 只能在 Windows 用。
4. 社区大 + 文档完善 遇到问题 Google 一下基本都是 Python 的解法。对实习生来说,这意味着卡住了能找到答案,不会死磕。
5. 跟 QA 工作高度匹配 QA 需要做 API 测试、UI 自动化、数据处理、日志分析——Python 在所有这些领域都有成熟的工具链。
拿我自己项目举例:我写 Smart Bakery 后端的时候,用 Python + FastAPI 构建了 RESTful API,然后用
pytest + requests写了自动化测试脚本。同样的需求如果用 Java + Spring Boot,光环境配置就要多花两三倍时间。Python 让我可以 快速验证想法,快速迭代。“
2. 你怎么用 Python 实现视频测试自动化?
面试官想听什么
| 考察点 | 具体内容 |
|---|---|
| ✅ 你理解视频测试的特殊性 | 不是测 UI,是测视频内容 |
| ✅ 你知道相关工具 | FFmpeg、OpenCV |
| ✅ 你有具体的方法论 | 不是笼统说”用Python测” |
回答模板
“视频测试自动化和普通 API 测试不一样——你不能直接 assert 视频对不对。我了解到的做法是这样的:
1. 用 FFmpeg 处理测试视频
# 裁剪视频:从第10秒开始取5秒 ffmpeg -i input.mp4 -ss 00:00:10 -t 5 output.mp4 # 抽帧:每秒取1帧,用于比对 ffmpeg -i input.mp4 -vf "fps=1" frames/frame_%04d.png # 获取视频元数据 ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate input.mp42. 用 OpenCV 做帧对比(检测画面异常)
import cv2 from skimage.metrics import structural_similarity as ssim def compare_frames(frame1_path, frame2_path): img1 = cv2.imread(frame1_path, cv2.IMREAD_GRAYSCALE) img2 = cv2.imread(frame2_path, cv2.IMREAD_GRAYSCALE) score, diff = ssim(img1, img2, full=True) return score # 0~1, 越接近1越相似 # 如果分数低于0.95,说明画面有明显差异 → 可能是Bug3. 视频分析场景的特殊测试维度
- 检测准确率:往视频里插入已知事件(比如人走过),看 AI 能不能检测到
- 延迟测试:在视频帧上打时间戳,测从事件发生到告警弹出用了多长时间
- 不同条件:用 FFmpeg 模拟不同亮度、不同分辨率、不同帧率的视频
- 流媒体稳定性:长时间播放(24h+)看会不会断流、花屏
4. 自动化测试框架结构
# conftest.py import pytest import subprocess from pathlib import Path @pytest.fixture def test_video_dir(): """准备测试视频的目录""" return Path("./test_videos") # test_analytics.py def test_detection_accuracy(test_video_dir): """验证AI检测准确率""" video = test_video_dir / "pedestrian_daytime.mp4" result = call_analysis_api(video) assert result["detections"] >= 1 # 至少检测到一个人 assert result["confidence"] > 0.8 # 置信度>80%总结:视频测试自动化的核心是 FFmpeg 处理视频 + OpenCV 帧对比 + Python 编排测试流程。虽然我在学校项目里还没做过真正的视频测试,但这些工具和思路我已经有所了解。“
3. Python 怎么配置 API 来做到自动化交互?
回答模板
“Python 做 API 自动化交互,最常用的组合是
requests+pytest。核心流程很简单:第1步:安装依赖
pip install requests pytest第2步:写 API 请求脚本(requests 库)
import requests BASE_URL = "http://192.168.1.166:5000" # GET 请求 — 获取状态 response = requests.get(f"{BASE_URL}/api/status", timeout=5) data = response.json() print(f"温度: {data['temperature']}°C") # POST 请求 — 控制设备 payload = {"device": "fan", "mode": "ON"} response = requests.post(f"{BASE_URL}/api/control", json=payload) assert response.status_code == 200第3步:用 pytest 组织测试用例
# test_api.py import requests import pytest BASE_URL = "http://192.168.1.166:5000" def test_status_returns_temperature(): response = requests.get(f"{BASE_URL}/api/status", timeout=5) assert response.status_code == 200 data = response.json() assert "temperature" in data assert isinstance(data["temperature"], float) def test_control_with_valid_params(): payload = {"device": "fan", "mode": "ON"} response = requests.post(f"{BASE_URL}/api/control", json=payload) assert response.status_code == 200 def test_control_with_invalid_device(): payload = {"device": "invalid_device", "mode": "ON"} response = requests.post(f"{BASE_URL}/api/control", json=payload) assert response.status_code == 400 # 预期错误 @pytest.mark.parametrize("device", ["fan", "buzzer"]) def test_all_devices(device): """参数化测试:同一个测试跑多个设备""" payload = {"device": device, "mode": "ON"} response = requests.post(f"{BASE_URL}/api/control", json=payload) assert response.status_code == 200第4步:用 fixture 管理共享资源
@pytest.fixture def base_url(): return "http://192.168.1.166:5000" @pytest.fixture def api_client(base_url): """创建一个带base_url的session,所有测试复用""" session = requests.Session() session.base_url = base_url return session第5步:运行
pytest test_api.py -v # 详细输出 pytest test_api.py -v --html=report.html # 生成HTML报告总结:Python 做 API 自动化交互就 三步走:requests 发请求 → pytest 组织测试 → 断言验证结果。简单直接,不需要复杂的框架。“
4. 为什么要测试视频?视频都是从哪来的?
为什么要测试视频?
1. 视频是核心产品功能 KAI 平台的核心就是分析视频。如果视频流有问题(延迟高、卡顿、花屏),AI 分析的基础就垮了。测试视频的质量和稳定性就是测试产品的核心功能。
2. 视频分析结果直接关系客户业务
- 安防场景:漏报一个入侵检测 → 安全隐患
- 零售场景:人数统计不准 → 商业决策错误
- 交通场景:车牌识别失败 → 执法问题 视频测试不只是测”画面好不好看”,而是测”业务正不正确”。
3. 视频有独特的测试维度
测试维度 普通App 视频产品 输入 点击、输入文字 视频流(持续不断的数据) 输出 UI变化、数据返回 检测结果、告警、元数据 环境因素 网络、设备 光照、天气、镜头角度、遮挡 性能指标 页面加载时间 端到端延迟、每秒处理帧数 4. 视频问题很难靠手动测试覆盖全 人眼看 24 小时监控录像不现实。自动化视频测试可以:批量跑不同场景的视频、每帧比对、量化检测准确率。
视频都是从哪来的?
视频来源(按 QA 使用场景):
┌──────────────────────────────────────────────┐
│ 1. 实验室录制 ← QA 测的最多的 │
│ └── 在实验室用测试摄像头对着测试场景 │
│ └── 场景:假人走路、玩具车、灯光变化 │
│ │
│ 2. 公开数据集 │
│ └── 开源视频数据集:COCO、ImageNet Vid │
│ └── 特定场景:停车场、商场、街道 │
│ │
│ 3. FFmpeg 合成/处理 ← QA 经常自己做 │
│ └── 转格式、裁剪、调亮度模拟不同条件 │
│ └── 场景:用 FFmpeg 把1080p压成720p │
│ │
│ 4. 客户现场录制 │
│ └── 客户部署环境的真实录像 │
│ └── 场景:客户说"晚上经常误报" │
│ │
│ 5. 预置测试视频 │
│ └── 公司内部积累的标准测试视频库 │
│ └── 每个版本回归测试都用同一批视频 │
└──────────────────────────────────────────────┘
作为实习生,你接触到的基本是 #1 和 #3。Senior QA 可能会给你一个 U 盘,里面装着标好号的测试视频,你按 test case 的要求跑就行。
5. 你知道怎么接摄像头吗?
面试官想听什么
不是问你”你会不会爬梯子装摄像头”,而是问 “你理解摄像头是怎么被添加进系统的”。
回答模板
“我理解的’接摄像头’不是去现场安装,而是在 KAI UP 平台上配置摄像头连接。流程大致是:
1. 物理连接(由现场工程团队负责)
- 摄像头接 PoE 网线(供电+网络一根线)
- 摄像头通电后获取 IP 地址
2. 软件配置(QA/工程师在网页操作)
登录 KAI UP → 摄像头管理 → + Add Camera ├── Name: 停车场入口 ├── Protocol: ONVIF ├── IP Address: 192.168.1.100 ├── Port: 80 ├── Username: admin ├── Password: **** └── Location: 1楼停车场3. 验证连接
- 点 “Test Connection” → 显示 “Connection Successful”
- 保存后摄像头状态变 “Online”
- 进 Live View 能看到画面
4. QA 要测什么
- 正确信息 → 能不能连接成功
- 错误信息 → 友好提示
- 断网重连 → 能不能自动恢复
- 多个摄像头同时添加 → 会不会卡顿
在我的 Smart Bakery 项目里,树莓派通过 GPIO 连接传感器的方式跟这个类似——都是 配置连接 → 验证通信 → 测试稳定性。只是 Smart Bakery 用的是 GPIO 引脚,KAI 用的是 ONVIF 协议。“
6. 你怎么用 SQL 来管理测试的数据?
回答模板
“在 QA 工作中,SQL 主要用来做 数据校验、测试数据准备、测试结果分析。
用法1:数据校验 测完一个功能后,去数据库确认数据是否正确写入。
-- 验证:添加摄像头后,数据库有没有正确记录 SELECT id, name, ip_address, status, created_at FROM cameras WHERE name = '测试摄像头-01'; -- 预期:一条记录,status = 'online' -- 验证:AI 检测到的告警有没有入库 SELECT camera_id, event_type, confidence, detected_at FROM alerts WHERE detected_at >= '2026-06-10' ORDER BY detected_at DESC;用法2:准备测试数据 手动创建一些数据,让测试环境有数据可用。
-- 插入10条测试告警记录 INSERT INTO alerts (camera_id, event_type, confidence, detected_at) VALUES (1, 'intrusion', 0.95, NOW()), (1, 'intrusion', 0.87, NOW() - INTERVAL 1 HOUR), (2, 'crowd', 0.92, NOW() - INTERVAL 2 HOUR);用法3:测试结果统计分析
-- 统计每个摄像头的检测数量 SELECT camera_id, COUNT(*) as total_alerts, AVG(confidence) as avg_confidence FROM alerts WHERE date(detected_at) = CURDATE() GROUP BY camera_id ORDER BY total_alerts DESC; -- 对比两个版本的测试结果 SELECT version, COUNT(*) as total_tests, SUM(CASE WHEN status = 'pass' THEN 1 ELSE 0 END) as passed, ROUND(SUM(CASE WHEN status = 'pass' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) as pass_rate FROM test_results WHERE test_suite = 'camera_add' GROUP BY version;用法4:清理测试数据
-- 测试完成后清理数据 DELETE FROM alerts WHERE camera_id IN (SELECT id FROM cameras WHERE name LIKE 'test_%'); DELETE FROM cameras WHERE name LIKE 'test_%';我的 SQL 基础:在 Smart Bakery 项目里,我设计了一个 MySQL 数据库来管理用户和设备配置文件。我能写基本的 CRUD 语句、聚合查询、多表 JOIN。对于 QA 场景需要的数据校验和统计分析,这些基础足够用了。“
7. 你知道测试的准则是什么吗?
回答模板
“我在项目和自学中理解到,软件测试有一些 基本原则(Testing Principles),不管用什么工具、测什么产品,都要遵循:
1. 测试只能证明有 Bug,不能证明没有 Bug 即使所有测试都通过了,也不能说软件”没有 Bug”。只能说你覆盖到的场景没问题,没测到的地方可能还有 Bug。
2. 尽早测试(Shift Left) Bug 发现得越晚,修的成本越高。需求阶段就能发现的问题,比上线后才发现,修复成本可能差几十倍。
3. 杀虫剂悖论(Pesticide Paradox) 同一批测试用例反复跑,就找不到新 Bug 了。需要定期 review 和更新测试用例,或者做探索性测试来发现新问题。
4. 缺陷集群性(Defect Clustering) Bug 不是均匀分布的——少数模块集中了大部分 Bug。测试时要关注那些历史上出问题多的模块(就像 Smart Bakery 里,GPIO 传感器读取那块比 API 层更容易出问题)。
5. 全覆盖是不可能的 测试所有可能的输入组合是不现实的。需要基于 风险评估 来决定测什么、不测什么——核心功能优先,边界条件覆盖,低风险区域适当放行。
6. 测试上下文相关 没有”放之四海皆准”的测试方法。安防系统的测试策略和电商 App 完全不同。
7. 测试的”三明治”策略 一个好的测试体系是金字塔形的:
/\ UI 测试(少量) / \ 集成/API 测试(适量) /____\ 单元测试(大量)我自己做项目的体会:在 Smart Bakery 项目里,我一开始只测”能不能连上”,后来发现”连上后数据准不准”是更大的问题。这让我理解了 测试不是做完就完了,而是不断迭代深化的过程。“
8. FastAPI 跟 RESTful API 的区别在哪?
面试官想问什么
面试官可能想问的是 FastAPI vs 其他 Web 框架(如 Flask)的区别,或者说FastAPI 如何帮助实现 RESTful API。
回答模板
“这个问题需要先区分一下概念:
RESTful API 是一种设计风格/规范,不是具体的工具。它定义了:
- 用 HTTP 方法(GET/POST/PUT/DELETE)表示操作
- URL 表示资源(
/api/cameras、/api/alerts/123)- 无状态通信
- 返回 JSON/XML
FastAPI 是一个实现 RESTful API 的 Python 框架,它的特点:
对比 FastAPI 传统 RESTful API 实现(如 Flask) 异步 原生支持 async/await 同步(WSGI),需要额外配置 数据校验 Pydantic 自动校验请求/响应 手动校验 文档 自动生成 Swagger UI ( /docs)需要手动集成 Flasgger 性能 基于 Starlette + Uvicorn,性能接近 Node.js/Go 相对较慢 类型提示 利用 Python 类型提示来做校验 不支持 用我的 Smart Bakery 项目举例:
# FastAPI 实现 RESTful API from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() # Pydantic 自动做数据校验 class ControlCommand(BaseModel): device: str # 如果不是字符串,自动返回422 mode: str class BakeryStatus(BaseModel): temperature: float humidity: float @app.get("/api/status", response_model=BakeryStatus) async def get_status(): # FastAPI 自动校验返回格式 temp, hum = read_sensor() return BakeryStatus(temperature=temp, humidity=hum) @app.post("/api/control") async def control_device(cmd: ControlCommand): # cmd 已经被 Pydantic 校验过了 if cmd.device not in VALID_DEVICES: raise HTTPException(status_code=400, detail="Invalid device") control_gpio(cmd.device, cmd.mode) return {"status": "ok"}用 Flask 写同样的功能,你需要自己写 if/else 校验参数、手动拼错误响应、手动写文档——代码量多一倍以上。
总结一句话:RESTful API 是做什么(规范),FastAPI 是怎么做(工具)。FastAPI 让实现 RESTful API 更高效、更安全、文档更完善。“
9. 你知道怎么找出 Bug 吗?找出后你要怎么处理?
怎么找出 Bug
“找 Bug 不是靠运气,而是靠 系统的方法:
方法1:按 Test Case 执行 最基础的方法。按写好的 test case一步步操作,预期结果和实际结果不一致 → Bug。
方法2:边界值测试 正常输入没问题,但边界条件容易出问题。
- 名字输入1个字符 → 行不行? - 名字输入200个字符 → 会不会崩溃? - IP 地址填 0.0.0.0 → 会不会出错? - 同时添加 50 个摄像头 → 会不会卡死?方法3:异常路径测试 用户不会按”标准操作”来用。你故意做”错”的操作。
- 不填任何字段直接点保存 - 填了密码但用户名不对 - 摄像头断电后看系统状态 - 快速双击提交按钮方法4:探索性测试 (Exploratory Testing) 不看 test case,凭直觉随便点。这是找到”没人想到”的 Bug 的最好方法。
方法5:从日志/监控数据中发现问题 系统没有报错,但 CPU 一直在涨、数据库记录不对——这些靠”点点点”发现不了,需要看日志和数据。
找出后怎么处理
第1步:复现确认 同一个操作再做一次,确认不是偶然问题。至少复现 3 次。
第2步:记录操作步骤 写清楚 做了什么 → 看到了什么 → 应该看到什么。
第3步:收集证据
- 截图/录屏
- 浏览器控制台报错(F12 → Console)
- 后端日志
- 测试环境信息(浏览器版本、系统版本、软件版本)
第4步:提 Bug 到 JIRA
Summary: 摄像头名称输入特殊字符导致页面崩溃 Priority: Major Steps to Reproduce: 1. 登录 KAI UP 2. 进入 摄像头管理 → Add Camera 3. Name 输入 "!@#$%^" 4. 点击 Save Actual: 页面白屏 Expected: 提示"名称包含非法字符" Attachment: [截图]第5步:跟踪
- 开发修好后,你验证修复(确认 Bug 真的好了)
- 如果没好 → Reopen
- 验证通过 → Close
第6步:思考同类问题 这个 Bug 暴露了什么问题?会不会类似的地方也有同样的 Bug?比如”名字有特殊字符崩溃”→ 是不是所有输入框都有这个问题?→ 批量检查。“
10. 你对自动化的流程怎么理解?具体会怎么实施?
回答模板
我对自动化流程的理解:
自动化测试不是”写了脚本就完事”,而是一个完整的闭环流程:
手动测试 → 发现重复 → 写自动化脚本 → 集成到CI/CD ↑ │ └──────── 维护更新 ← 发现问题 ← 自动运行 ─┘我具体会怎么实施(以 API 测试为例):
阶段1:分析哪些适合自动化
✅ 适合自动化:回归测试、冒烟测试、API 测试 ❌ 不适合自动化:探索性测试、UI 视觉检查、一次性测试阶段2:选工具
# 我选的组合 Python + requests + pytest # 理由:语法简单、生态好、团队可能已经在用阶段3:搭框架
tests/ ├── conftest.py # 共享的fixture(Token、Base URL) ├── test_cameras.py # 摄像头相关测试 ├── test_alerts.py # 告警相关测试 ├── test_analytics.py # 视频分析测试 ├── data/ │ └── test_data.json # 测试数据 └── reports/ # 测试报告输出阶段4:从核心功能开始
第1批:登录 + 获取摄像头列表 (GET) → 最基础,先保证能跑通 第2批:添加/删除/修改摄像头 (POST/DELETE/PUT) 第3批:告警查询 + 视频分析结果 第4批:异常输入 + 边界条件阶段5:集成 CI/CD
# .github/workflows/test.yml name: API Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: pip install -r requirements.txt - run: pytest tests/ -v --html=report.html阶段6:维护
- 每次 API 更新 → 同步更新测试脚本
- 失败用例 → 分析是 Bug 还是脚本过期
- 定期 review 测试覆盖率
11. 如果要优化自动化,你有什么提议?
回答模板
“如果我进团队后发现现有自动化有优化的空间,我会从这几个方向提建议:
1. 先看数据,再说优化
不问"我们优化一下吧",而是先问: - 当前自动化覆盖率是多少? - 平均执行时间多长? - 失败率多少?失败是因为真的 Bug 还是脚本不稳定?2. 降低接入门槛
- 写清晰的 README:装什么、怎么跑、怎么看结果
- 测试用例用中文描述清楚预期结果
- 一键运行:
pytest tests/ -v3. 优先自动化高频重复的手动测试
- 每次发版前都要跑的那 50 个 test case → 最有价值
- 不用一上来就追求 100% 覆盖,先解决最痛的
4. 加参数化测试
# 原来:一个设备写一个测试 def test_fan_on(): ... def test_buzzer_on(): ... # 优化后:参数化 @pytest.mark.parametrize("device,mode", [ ("fan", "ON"), ("fan", "OFF"), ("buzzer", "ON"), ("buzzer", "OFF"), ]) def test_control_device(device, mode): # 一行代码覆盖4个场景5. 加 CI/CD 触发
- 每次 Pull Request 自动跑核心测试
- 面试官在 NCS 做了这个,他一定认可
6. 测试报告可视化
- 从 pytest 的输出变成 HTML/Allure 报告
- 失败用例自动截图/录屏
- 趋势图:这个版本的通过率比上个版本高还是低
12. 你怎么用 Bash/PowerShell 脚本实现自动化?
回答模板
“在 QA 工作中,Bash/PowerShell 脚本主要用来做 环境准备、部署、日志收集、定时任务 等运维层面的自动化。“
Bash 脚本例子
1. 一键部署测试环境(来自 Smart Bakery)
#!/bin/bash # deploy.sh — 部署 Smart Bakery 后端到树莓派 set -e # 任何一步失败就停止 echo "=== 开始部署 ===" HOST="pi@192.168.1.166" REMOTE_DIR="/home/pi/smart-bakery" # 1. 同步代码到树莓派 rsync -avz --exclude='venv' ./ $HOST:$REMOTE_DIR/ # 2. 安装依赖 ssh $HOST "cd $REMOTE_DIR && pip install -r requirements.txt" # 3. 重启服务 ssh $HOST "sudo systemctl restart smart-bakery" # 4. 健康检查 sleep 3 curl -s http://192.168.1.166:5000/health || echo "❌ 部署失败" echo "=== 部署完成 ==="2. 批量测试
# run_tests.sh #!/bin/bash echo "========== 运行测试套件 ==========" echo "时间: $(date)" # 激活虚拟环境 source venv/bin/activate # 运行 API 测试 echo "1. API 测试..." pytest tests/test_api.py -v --tb=short # 运行视频处理测试 echo "2. 视频测试..." python scripts/compare_video.py --source source.mp4 --output result.json # 生成报告 echo "3. 生成报告..." pytest tests/ -v --html=report.html echo "========== 测试完成 =========="3. 日志收集
# collect_logs.sh #!/bin/bash LOG_DIR="logs/$(date +%Y%m%d_%H%M%S)" mkdir -p $LOG_DIR # 收集系统日志 journalctl -u kai-up --since "24 hours ago" > $LOG_DIR/system.log # 收集应用日志 docker logs kai-api > $LOG_DIR/api.log 2>&1 # 打包 tar -czf $LOG_DIR.tar.gz $LOG_DIR/
PowerShell 对应的命令
Linux Bash Windows PowerShell lsGet-ChildItem或lsgrepSelect-StringcurlInvoke-WebRequest或curlssh user@hostssh user@hostchmod +x script.sh不需要(Windows 按扩展名 .ps1 识别) ./script.sh.\script.ps1
13. 你知道怎么运行脚本吗?
面试官问的潜台词
不是真的问”你知道双击文件吗”,而是问:
你知道脚本运行需要什么环境?知道怎么管理依赖?知道怎么处理错误?
回答模板
“知道。运行脚本不只是
python xxx.py那么简单,有几个要点:1. 环境准备
# 创建虚拟环境(隔离依赖) python -m venv venv source venv/bin/activate # Linux/macOS .\venv\Scripts\activate # Windows # 安装依赖 pip install -r requirements.txt # 确认安装成功 pip list python --version2. 设置执行权限(Linux)
chmod +x script.sh # 给脚本执行权限 ./script.sh # 运行3. 传入参数
python test_api.py --env staging --browser chrome --headless # 脚本内部用 argparse 或 sys.argv 接收4. 环境变量
# 把敏感信息放环境变量,不写死在代码里 export API_TOKEN="xxxx" export DB_PASSWORD="xxxx" python test_api.py5. 处理错误
# 脚本出错时自动退出 set -e # bash # 或者在 Python 中 # 用 try/except 捕获 # 用 exit(1) 返回非零退出码告诉CI"失败了"6. 一键运行
# Makefile test: pip install -r requirements.txt pytest tests/ -v --html=report.html clean: rm -rf __pycache__ .pytest_cache
14. 你对 Linux 了解多少?
回答模板
“Linux 是我日常开发用的主力系统。我在 Smart Bakery 项目里全程在 Ubuntu 上工作。具体了解和常用的有:
日常操作(熟练)
ls -la # 查看文件详情 cd /path/to/dir # 切换目录 cp -r source dest # 复制 mv source dest # 移动/重命名 rm -rf dir/ # 删除 mkdir -p a/b/c # 创建多级目录 chmod +x file # 给权限 chown user:group file # 改所有者 ps aux | grep python # 查看进程 kill -9 PID # 强制杀进程文件处理
cat file.log # 看文件 less file.log # 分页查看 tail -f file.log # 实时跟踪日志 head -n 100 file.log # 看前100行 grep "error" *.log # 搜索关键词 wc -l file.log # 统计行数 sort # 排序 uniq # 去重网络
curl http://localhost:5000/api/status # HTTP 请求 ping 192.168.1.1 # 网络连通性 ifconfig 或 ip addr # 查看IP netstat -tlnp # 查看端口占用 ssh pi@192.168.1.166 # 远程登录树莓派 scp file.txt pi@192.168.1.166:/home/pi/ # 传文件 rsync -avz ./ pi@192.168.1.166:/remote/ # 同步目录系统管理
systemctl start/stop/restart service-name # 服务管理 systemctl enable service-name # 开机自启 journalctl -u service-name -f # 查看服务日志 df -h # 磁盘空间 free -h # 内存使用 top 或 htop # 系统监控文本处理
# 用管道组合命令,一个需求一行解决 cat access.log | grep "ERROR" | awk '{print $1}' | sort | uniq -c | sort -nr | head -10 # ↑ 统计访问日志中错误最多的前10个IP总结:我能在 Linux 上独立完成开发、部署、排错。虽然没到运维级别,但日常工作和 QA 测试需要的命令都熟悉。如果面试官想考我某个具体命令,我可以当场试一下。“
15. 你对 Docker, Kubernetes 了解多少?
回答模板
“我了解 Docker 和 Kubernetes 的基本概念和使用场景,但在实际项目中只用过 Docker。
Docker 的理解:
Docker = 把应用和依赖打包成一个”集装箱”,在哪都能跑,保证环境一致。
┌─────────────────────────────┐ │ Docker 容器 │ │ ┌───────────────────────┐ │ │ │ 你的应用 (Python) │ │ │ │ 依赖库 (pip包) │ │ │ │ 配置文件 │ │ │ │ 操作系统层 (精简版) │ │ │ └───────────────────────┘ │ │ 可移植 ✓ │ └─────────────────────────────┘Docker 的核心概念(用类比):
镜像 (Image) = 做蛋糕的配方(只读模板) 容器 (Container) = 按配方做出来的蛋糕(运行中的实例) Dockerfile = 配方说明书(告诉Docker怎么构建) Docker Hub = 配方分享网站(下载别人的镜像) Docker Compose = 同时做蛋糕+泡咖啡(管理多个容器)我实际用过的 Docker 操作:
# 基本操作 docker pull python:3.11 # 下载 Python 镜像 docker build -t my-app . # 构建镜像 docker run -d -p 5000:5000 my-app # 运行容器 docker ps # 查看运行中的容器 docker logs -f container_id # 查看容器日志 docker stop container_id # 停止容器 # 用 Docker Compose 同时启动多个服务 # docker-compose.yml version: '3' services: api: build: . ports: - "5000:5000" db: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: test为什么 QA 需要 Docker:
传统问题: "在我电脑上是好的啊!" Docker 解决:环境一致性——测试环境跟生产环境用同一份镜像 传统问题: 装 MySQL / Redis 很麻烦 Docker 解决: docker run mysql:8 一行命令启动 传统问题: 测试完环境乱了 Docker 解决:容器删了重建,永远是干净状态对 Kubernetes 的了解(概念级别,没有实操):
K8s = 管理 Docker 容器的”管家”——自动部署、自动扩容、自动恢复。
类比:Docker = 每个员工有自己的工位 K8s = 一个公司(有人离职自动招人、忙的时候加人、闲的时候减人)K8s 核心概念:
Pod = 一个或多个容器(最小单位) Deployment = 告诉K8s"我要跑3个副本" Service = 固定的访问地址(不管Pod怎么变) Namespace = 隔离不同环境(dev/test/prod)学习计划: 我目前 Docker 基本操作没问题。Kubernetes 我了解概念,但缺实操经验。如果团队在用 K8s,我愿意花时间学习。之前在 NCS 的 JD 里看到你们也用 Docker 和 K8s,这也是我来这个岗位想提升的方向。“
16. 你是怎么配置后端与数据库交互的?
面试官想听什么
| 考察点 | 具体内容 |
|---|---|
| ✅ 你理解后端 ≠ 数据库,中间需要一层连接 | 知道 ORM 或驱动层 |
| ✅ 你知道怎么选连接方式 | 根据项目规模选合适方案 |
| ✅ 你有实际配置经验 | 不是只会理论 |
| ✅ 你考虑过安全问题 | SQL 注入、密码管理 |
回答模板
“这个问题得先说一下我项目里实际的数据库使用情况:
Smart Bakery 后端本身的特殊性
Smart Bakery 的核心功能是 实时传感器监控 + 硬件控制,所以大部分数据是实时数据,不需要持久化到数据库——温湿度每 800ms 读取一次,直接通过 API 返回给前端,不存库。
但这不代表我没做过数据库交互。我做过两层:
第一层:用户配置管理系统(MySQL)
在项目的 用户资料和设备配置管理 部分,我用到了 MySQL 数据库。设计了三张表:
-- 用户表:管理登录用户 CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, email VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 设备表:管理的IoT设备 CREATE TABLE devices ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, device_name VARCHAR(100) NOT NULL, device_type ENUM('sensor', 'actuator') NOT NULL, gpio_pin INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); -- 告警规则表 CREATE TABLE alert_rules ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, device_id INT NOT NULL, condition_type ENUM('above', 'below') NOT NULL, threshold_value FLOAT NOT NULL, is_active BOOLEAN DEFAULT TRUE, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (device_id) REFERENCES devices(id) );
第二层:FastAPI 连接数据库(Python + mysql-connector)
# database.py — 数据库连接配置 import mysql.connector from mysql.connector import pooling # 连接池配置(避免每次请求都新建连接) db_pool = mysql.connector.pooling.MySQLConnectionPool( pool_name="smart_bakery_pool", pool_size=5, host="localhost", user="app_user", password="secure_password", # 实际用环境变量 database="smart_bakery" ) def get_db(): """获取数据库连接(作为FastAPI依赖使用)""" conn = db_pool.get_connection() try: yield conn finally: conn.close() # 用完归还连接池# models.py — 数据操作层 from fastapi import Depends def get_user_devices(user_id: int, db=Depends(get_db)): """查询某个用户的所有设备""" cursor = db.cursor(dictionary=True) cursor.execute( "SELECT * FROM devices WHERE user_id = %s", (user_id,) # 参数化查询,防SQL注入 ) return cursor.fetchall() def create_alert_rule(rule_data: dict, db=Depends(get_db)): """创建告警规则""" cursor = db.cursor() cursor.execute( """INSERT INTO alert_rules (user_id, device_id, condition_type, threshold_value) VALUES (%s, %s, %s, %s)""", (rule_data["user_id"], rule_data["device_id"], rule_data["condition_type"], rule_data["threshold_value"]) ) db.commit() return cursor.lastrowid
第三层:FastAPI 接口调用数据库
# main.py — API 接口 from fastapi import FastAPI, Depends, HTTPException from pydantic import BaseModel app = FastAPI() # Pydantic 校验输入 class AlertRuleCreate(BaseModel): user_id: int device_id: int condition_type: str # "above" or "below" threshold_value: float @app.post("/api/alert-rules") async def create_alert(rule: AlertRuleCreate): """ POST /api/alert-rules 请求体: {"user_id": 1, "device_id": 2, "condition_type": "above", "threshold_value": 40.0} """ rule_id = create_alert_rule(rule.dict()) return {"rule_id": rule_id, "status": "created"} @app.get("/api/users/{user_id}/devices") async def get_devices(user_id: int): """ GET /api/users/1/devices 返回该用户的所有设备列表 """ devices = get_user_devices(user_id) if not devices: raise HTTPException(status_code=404, detail="No devices found") return {"devices": devices}
完整数据流
客户端(Flutter App)
│ POST /api/alert-rules ← JSON 请求体
▼
FastAPI 应用
│ 1. Pydantic 校验输入数据格式
│ 2. Depends(get_db) 注入数据库连接
▼
数据库连接池
│ 3. 从连接池拿一个可用连接
▼
MySQL 数据库
│ 4. 执行 INSERT 语句(参数化查询)
│ 5. 返回自增 ID
▼
FastAPI 应用
│ 6. 组装响应 {"rule_id": 5, "status": "created"}
▼
客户端 ← JSON 响应
配置要点总结
| 要点 | 具体做法 | 为什么重要 |
|---|---|---|
| 连接池 | MySQLConnectionPool(pool_size=5) | 不用每次请求新建/关闭连接,性能提升明显 |
| 参数化查询 | WHERE id = %s 而不是 f-string 拼接 | 防 SQL 注入——这是最基本的数据库安全意识 |
| 依赖注入 | FastAPI 的 Depends(get_db) | 自动管理连接生命周期,用完自动归还 |
| 环境变量 | 密码从环境变量读取,不硬编码 | 防止密码泄露到 Git 仓库 |
| Pydantic 校验 | 先校验再入库 | 确保数据库只收到合法数据 |
如果面试官追问 “你用 ORM 吗?”
“Smart Bakery 项目比较简单,我直接用了 SQL 语句 + mysql-connector,更直观。但如果项目复杂度上来,比如表很多、关系复杂,我会用 SQLAlchemy 或 Tortoise ORM。ORM 的好处是:
- 不用写 SQL,用 Python 对象操作数据库
- 自动建表 migration
- 跨数据库兼容(MySQL / PostgreSQL / SQLite 切换方便)
举个例子,如果用 SQLAlchemy,上面的代码会更简洁:
from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import Session # 定义模型 class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) username = Column(String(50), unique=True) # 查询 db = Session(engine) user = db.query(User).filter(User.username == "admin").first()对于实习岗位来说,两种方式我都理解,可以根据团队的技术栈快速切换。“
17. 你对 pytest,软件测试了解多少?
面试官想听什么
| 考察点 | 具体内容 |
|---|---|
| ✅ 你用过 pytest,不只是听说过 | 能说出具体语法 |
| ✅ 你理解 pytest 的核心特性 | fixture、parametrize、conftest 等 |
| ✅ 你懂软件测试的分类 | 不只是”点点点” |
| ✅ 你有测试思维 | 知道测什么、怎么测、为什么测 |
回答模板
“pytest 是我在 Python 项目里最常用的测试框架,我在 Smart Bakery 项目里用它写过 API 自动化测试。让我从 pytest 本身 和 软件测试整体 两个角度来说。“
Part 1:pytest 掌握程度
1. 基本用法
# pytest 自动发现以 test_ 开头的函数和文件 def test_status_returns_200(): response = requests.get("http://localhost:5000/api/status") assert response.status_code == 2002. Fixture(共享测试资源)
import pytest import requests @pytest.fixture def base_url(): """提供API基础地址,所有测试复用""" return "http://localhost:5000" @pytest.fixture def client(base_url): """创建一个带超时设置的session""" session = requests.Session() session.headers.update({"Content-Type": "application/json"}) yield session session.close() # 测试结束后清理 # fixture 的 scope 控制生命周期 # scope="function" → 每个测试函数重新创建(默认) # scope="module" → 每个模块创建一次 # scope="session" → 整个测试过程只创建一次3. 参数化测试
@pytest.mark.parametrize("device,expected_status", [ ("fan", 200), ("buzzer", 200), ("invalid_device", 400), # 非法设备→预期400 ("", 422), # 空字符串→预期422 ]) def test_control_device(base_url, device, expected_status): """一组数据跑一个测试,覆盖多个场景""" payload = {"device": device, "mode": "ON"} response = requests.post(f"{base_url}/api/control", json=payload) assert response.status_code == expected_status4. conftest.py(共享配置)
# conftest.py — pytest 自动加载,所有测试文件都能用 import pytest import requests @pytest.fixture(scope="session") def base_url(): return "http://localhost:5000" @pytest.fixture def api_client(base_url): """全局的 API 客户端""" client = requests.Session() client.base_url = base_url yield client client.close()5. 常用插件
插件 作用 pytest-html生成 HTML 测试报告 pytest-xdist并行运行测试( -n auto)pytest-cov测试覆盖率统计 pytest-mockMock 外部依赖 pytest-timeout设置单个测试超时时间 运行 pytest 的常用命令
pytest # 运行所有测试 pytest test_api.py # 运行指定文件 pytest test_api.py -v # 详细输出 pytest test_api.py -k "login" # 只运行名字包含 login 的测试 pytest test_api.py --html=report.html # 生成 HTML 报告 pytest -n auto # 并行运行(需 xdist 插件) pytest --cov=src tests/ # 带覆盖率统计6. 断言
assert response.status_code == 200 # 状态码 assert response.elapsed.total_seconds() < 2 # 响应时间 < 2秒 assert "temperature" in response.json() # 字段存在 assert isinstance(data["temperature"], float) # 字段类型正确 assert data["count"] > 0 # 数量 > 0
Part 2:软件测试的整体理解
测试分类(按粒度从细到粗):
手动测试 ← 人在点 ↓ 单元测试 (Unit Test) ← 测一个函数、一个方法 ↓ 用 pytest 测一个 API 端点 集成测试 (Integration Test) ← 测多个模块一起工作 ↓ 后端 + 数据库 + 外部API 端到端测试 (E2E Test) ← 模拟真实用户操作 ↓ 从登录到操作完整流程 验收测试 (Acceptance Test) ← 客户确认需求是否满足测试类型(按测什么分):
类型 测什么 例子 功能测试 功能对不对 添加摄像头能不能成功 UI 测试 界面好不好用 按钮位置、颜色、响应 API 测试 接口正不正确 返回的 JSON 格式对不对 性能测试 响应快不快 100 个摄像头同时检测 压力测试 极限扛不扛得住 1000 路视频流涌入 安全测试 有没有漏洞 SQL 注入、越权访问 回归测试 改坏了没 新版本老功能还能不能用 测试设计方法:
等价类划分 (Equivalence Partitioning) 把输入分成几类,每类选一个代表测 例:温度范围 0-50°C → 测 0、25、50 就够了 边界值分析 (Boundary Value Analysis) 容易在边界出问题,所以重点测边界 例:名字最长 50 字符 → 测 49、50、51 个字符 因果图法 (Cause-Effect) 分析什么条件组合导致什么结果 例:摄像头离线 + 告警开启 → 应该触发通知我的实践经验(Smart Bakery 项目):
我在 Smart Bakery 里用 pytest 做了这些事:
# 1. 功能测试 — 验证 API 能否正常工作 def test_control_fan_turns_on(): # 先关 → 再开 → 验证状态正确 requests.post(URL, json={"device": "fan", "mode": "OFF"}) response = requests.post(URL, json={"device": "fan", "mode": "ON"}) assert response.json()["fan_state"] == "ON" # 2. 异常测试 — 验证错误处理 def test_invalid_device_returns_400(): response = requests.post(URL, json={"device": "nuclear_reactor", "mode": "ON"}) assert response.status_code == 400 assert "error" in response.json() # 3. 边界测试 — 验证极端输入 def test_extreme_temperature(): # DHT22 传感器读数范围 -40°C ~ 80°C # 验证后端对极端读数的处理 response = requests.get(f"{URL}/api/status") temp = response.json()["temperature"] assert -40 <= temp <= 80 # 不可能超出传感器范围 # 4. 超时测试 — 验证传感器读取超时处理 def test_sensor_timeout_returns_error(): # 如果传感器读不到数据,应该返回错误而非死循环 response = requests.get(f"{URL}/api/status", timeout=10) assert response.status_code in [200, 503] # 要么成功,要么报告服务不可用对软件测试的理解总结:
- 测试不是为了证明没有 Bug,而是为了降低风险 — 不可能测完所有情况
- 自动化测试要快 — 如果跑一次要 1 小时,开发就不愿意跑了
- 测试用例要稳定 — 同一个代码跑 10 次应该 10 次都通过,不稳定比没测还差
- 测试是代码 — 跟生产代码一样需要 review、维护、重构
- 尽早介入 — 需求阶段就想怎么测,比开发完了再补测试高效得多
18. 你对数据库 ORM 有什么了解?它担任什么角色?
面试官想听什么
| 考察点 | 具体内容 |
|---|---|
| ✅ 你理解 ORM 存在的意义 | 不是”会用”,是”为什么需要” |
| ✅ 你能讲清楚 ORM 在架构中的位置 | 三层架构中的”中间层” |
| ✅ 你知道 ORM 的优点和缺点 | 不盲目吹捧 |
| ✅ 你有实际使用或比较的经验 | 能说出跟原生 SQL 的差别 |
回答模板
“ORM 全称 Object-Relational Mapping(对象关系映射),它的核心作用就是一句话:让开发者用 Python 对象的方式操作数据库,不用直接写 SQL 语句。“
1. ORM 在后端与数据库之间的角色
看这张图就清楚了:
┌─────────────────────────────────────────────────────────┐ │ 传统方式 │ │ │ │ 后端代码 数据库 │ │ ┌──────────┐ SQL 语句 ┌──────────┐ │ │ │ Python │ ─────────────→ │ MySQL │ │ │ │ 代码 │ ←───────────── │ 表+行 │ │ │ └──────────┘ 查询结果(元组) └──────────┘ │ │ ↑ │ │ 需要手动转换:元组 → Python 对象 │ │ 需要手动写: INSERT, SELECT, JOIN │ │ │ │ ─────────────────────────────────────────────────────── │ │ │ │ 使用 ORM │ │ │ │ 后端代码 ORM (SQLAlchemy) 数据库 │ │ ┌──────────┐ ┌──────────────────┐ ┌──────────┐ │ │ │ Python │ → │ User.query.all() │ → │ MySQL │ │ │ │ 代码 │ ← │ User(name="...") │ ← │ 表+行 │ │ │ └──────────┘ └──────────────────┘ └──────────┘ │ │ ↑ ↑ │ │ 操作Python对象 自动转换:对象 ↔ SQL │ │ 不需要写SQL 自动处理 JOIN/WHERE/INSERT │ └─────────────────────────────────────────────────────────┘ORM 的角色 = 翻译官
你写的 Python 对象 ──→ ORM 翻译 ──→ 数据库 SQL 语句 User(name="admin") ──→ INSERT INTO users (name) VALUES ('admin')没有 ORM 的时候你要做:
# 1. 写 SQL cursor.execute("SELECT * FROM users WHERE username = %s", ("admin",)) # 2. 拿到的是元组 result = cursor.fetchone() # (1, "admin", "hashed_pw", "admin@email.com") # 3. 手动转成对象 user = User(id=result[0], name=result[1], ...) # 还要记住第几列是什么有 ORM 的时候:
# 直接操作 Python 对象 user = session.query(User).filter(User.username == "admin").first() print(user.email) # 直接 .属性 访问,不用记列索引
2. ORM 的核心功能
功能 说明 代码例子 表映射 Python 类 = 数据库表 class User(Base): __tablename__ = "users"CRUD 增删改查用 Python 方法 db.add(user),db.commit()关系 外键 → Python 属性访问 user.devices自动 JOIN迁移 自动同步表结构变化 alembic upgrade head连接池 自动管理数据库连接 内建,不用自己写 方言兼容 换数据库不改代码 MySQL ↔ PostgreSQL 切换
3. 用 ORM 写 Smart Bakery 的例子
# models.py — 用 SQLAlchemy 定义数据库表 from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, ForeignKey from sqlalchemy.orm import declarative_base, relationship, Session Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) username = Column(String(50), unique=True, nullable=False) email = Column(String(100)) # 关系:一个用户有多个设备 devices = relationship("Device", back_populates="owner") class Device(Base): __tablename__ = "devices" id = Column(Integer, primary_key=True) name = Column(String(100), nullable=False) gpio_pin = Column(Integer) user_id = Column(Integer, ForeignKey("users.id")) # 关系:设备属于一个用户 owner = relationship("User", back_populates="devices") class AlertRule(Base): __tablename__ = "alert_rules" id = Column(Integer, primary_key=True) device_id = Column(Integer, ForeignKey("devices.id")) condition = Column(String(20)) # "above" / "below" threshold = Column(Float) is_active = Column(Boolean, default=True)# main.py — FastAPI + SQLAlchemy from fastapi import FastAPI, Depends from sqlalchemy.orm import Session app = FastAPI() # 依赖:获取数据库session def get_db(): db = Session(engine) try: yield db finally: db.close() @app.get("/api/users/{user_id}/devices") async def get_user_devices(user_id: int, db: Session = Depends(get_db)): """ 查询用户的所有设备 不用写 JOIN,直接 .devices 访问 """ user = db.query(User).filter(User.id == user_id).first() if not user: return {"error": "User not found"} # ORM 自动处理了 JOIN 查询 return { "username": user.username, "devices": [ {"id": d.id, "name": d.name, "gpio_pin": d.gpio_pin} for d in user.devices ] } @app.post("/api/alert-rules") async def create_alert_rule( device_id: int, condition: str, threshold: float, db: Session = Depends(get_db) ): """不用写 INSERT,直接创建对象""" rule = AlertRule( device_id=device_id, condition=condition, threshold=threshold ) db.add(rule) # → ORM翻译成 INSERT INTO alert_rules ... db.commit() # → 提交事务 db.refresh(rule) # → 拿到自增 ID return {"rule_id": rule.id, "status": "created"}
4. ORM 的优点
优点 解释 对 QA 的意义 开发效率高 不用写 SQL,对象操作更直观 测试代码也更简洁 防 SQL 注入 ORM 自动参数化,不需要手动拼接 少了一个安全漏洞来源 代码可读性强 User.name == "admin"比WHERE name = 'admin'更 Pythonic测试维护更容易 数据库无关 换数据库改一行配置就行 QA 可以在本地用 SQLite 测试 自动迁移 Alembic 自动管理表结构变更 测试环境同步方便 关系管理 .user.devices自动 JOIN测试数据准备更直观
5. ORM 的缺点(面试官可能追问)
这个问题很关键——知道缺点比知道优点更能体现你的深入理解。
缺点 说明 应对 性能开销 ORM 生成的 SQL 可能不是最优的 复杂查询用原生 SQL or text()N+1 查询问题 循环查询关联对象时产生大量 SQL 用 joinedload()预加载学习曲线 ORM 本身有 API 要学 SQLAlchemy 的 session/query 概念需要时间 隐式行为 不知道 ORM 在背后做了什么 开 echo=True看生成的 SQL 日志复杂查询难表达 多层嵌套 JOIN 用 ORM 反而麻烦 复杂报表直接用 SQL 关于 N+1 问题的例子:
# ❌ N+1 问题:先查100个用户,再每个用户查一次设备 for user in db.query(User).all(): # 1 次查询 print(user.devices) # N 次查询(每个用户多一次) # 总共:1 + 100 = 101 次 SQL 查询! # ✅ 预加载解决 from sqlalchemy.orm import joinedload users = db.query(User).options(joinedload(User.devices)).all() # 总共:1 次查询(JOIN 一次性查出)
6. 原生 SQL vs ORM 的选型对比
┌──────────────────────────────────────────────┐ │ 选原生 SQL 的场景 │ │ • 查询极度复杂(多层聚合报表) │ │ • 对性能要求极致(毫秒级响应) │ │ • 只需要执行很少的查询类型 │ │ • 团队 SQL 能力远大于 ORM 能力 │ │ 例子:数据仓库、实时看板、简单 CRUD API │ ├──────────────────────────────────────────────┤ │ 选 ORM 的场景 │ │ • 有 10+ 张表且关系复杂 │ │ • 需要频繁迁移数据库版本 │ │ • 团队有多个项目用不同数据库 │ │ • 开发效率优先于极致性能 │ │ 例子:Web 应用、SaaS 平台、CMS 系统 │ ├──────────────────────────────────────────────┤ │ 折中方案:两者混用 │ │ • 80% CRUD 用 ORM │ │ • 20% 复杂统计/报表用原生 SQL │ │ • SQLAlchemy 支持 `text("SELECT ...")` 混写 │ └──────────────────────────────────────────────┘
7. 对 QA 来说,理解 ORM 有什么用?
1. 写测试数据更方便
# 不用写 INSERT 语句 user = User(name="test_user") device = Device(name="test_camera", user_id=user.id) db.add_all([user, device]) db.commit()2. 测试数据库无关
# 本地测试用 SQLite(不需要装 MySQL) engine = create_engine("sqlite:///test.db") # CI 用 PostgreSQL engine = create_engine("postgresql://user:pass@host/db") # 改一行配置,所有 ORM 代码不用动3. 更容易理解 Bug 根因 QA 看到
N+1 query或者cartesian product导致的性能问题,能跟开发在同一个层面沟通。总结:ORM 是一个 抽象层,它让开发者用面向对象的思维操作数据库,提升了开发效率,但也不是银弹。最好的实践是 ORM 做 80% 的常规操作,原生 SQL 处理 20% 的特殊场景。
附录:回答策略速查
| 问题类别 | 核心策略 | 必说关键词 |
|---|---|---|
| Python 相关 | 举 Smart Bakery 例子 + 具体代码 | requests, pytest, FastAPI |
| 视频测试 | FFmpeg + OpenCV + 测试维度 | FFmpeg抽帧, SSIM比对 |
| API 相关 | requests + pytest + 断言 | status_code, json(), assert |
| 测试流程 | 手动 → 自动化 → CI/CD 闭环 | 回归测试, 冒烟测试 |
| Linux | 分类说(文件/网络/系统) | systemctl, journalctl, grep, curl |
| Docker | 类比解释 + 基础命令 | 镜像/容器, docker-compose |
| Bug 处理 | 复现 → 记录 → 提JIRA → 验证 | Steps to Reproduce |
| SQL | 数据校验 + 测试准备 + 结果分析 | SELECT, INSERT, DELETE, JOIN |
| 后端与数据库 | 连接池 + 参数化查询 + Pydantic校验 | 连接池, SQL注入, ORM |
| pytest 与软件测试 | fixture/parametrize + 测试分类 + 设计方法 | fixture, conftest, 等价类, 边界值 |
| ORM 与数据库 | 对象关系映射 + 角色 + 优劣势 | SQLAlchemy, N+1, 抽象层 |
| 自动化优化 | 先看数据 → 优先高频 → 参数化 | 覆盖率, 执行时间 |
| 测试准则 | 7个原则挑4-5个说 | 杀虫剂悖论, Shift Left |
说明:本文档覆盖 19 个面试最可能问到的技术/流程问题,每个问题提供了完整的回答模板。面试时不要原文背诵,而是参考里面的结构和关键点,用自己的话自然表达。 最后更新: 2026-06-10