OKEx for Android
283.16MB · 2025-09-16
持续集成(Continuous Integration,简称CI)是一种软件开发实践:开发人员频繁将代码提交到共享仓库(如GitHub的main分支),CI工具自动触发构建、测试流程,尽早发现代码中的错误。其核心目标是“快速反馈”——避免代码集成时出现大规模冲突,或因单个提交破坏整个项目的功能。
对于FastAPI项目而言,CI的价值在于:
GET /items/
、POST /users/
),确保接口功能正常;目前主流的CI工具包括GitHub Actions、GitLab CI、Jenkins等。本文以GitHub Actions为例(因多数FastAPI项目托管在GitHub,配置简单且免费)。
GitHub Actions的核心概念:
.github/workflows/
文件夹中;以下是一个完整的FastAPI CI工作流文件(.github/workflows/ci.yml
),涵盖“代码拉取→依赖安装→测试→构建Docker镜像”:
name: FastAPI CI # 工作流名称
on: # 触发条件:Push到main分支或PR到main分支
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test: # 定义一个名为build-and-test的Job
runs-on: ubuntu-latest # 运行在Ubuntu最新版本的虚拟环境中
steps:
# 步骤1:拉取项目代码(必须第一步,否则后续步骤无代码可操作)
- name: Checkout code
uses: actions/checkout@v4 # 使用官方的checkout动作,拉取代码到虚拟环境
# 步骤2:设置Python环境(匹配项目的Python版本)
- name: Set up Python 3.11
uses: actions/setup-python@v5 # 官方的Python环境配置动作
with:
python-version: "3.11" # 指定Python版本(需与项目依赖一致)
# 步骤3:安装项目依赖(使用requirements.txt)
- name: Install dependencies
run: | # 运行Shell命令
python -m pip install --upgrade pip # 升级pip到最新版
pip install -r requirements.txt # 安装requirements.txt中的依赖
# 步骤4:运行Pytest测试(生成JUnit格式的测试报告)
- name: Run tests with Pytest
run: pytest --junitxml=test-results.xml # 运行测试并生成JUnit报告(方便后续查看)
# 步骤5:构建Docker镜像(可选,若项目用Docker部署)
- name: Build Docker image
run: docker build -t my-fastapi-app:${{ github.sha }} . # 构建镜像,标签用Git提交的SHA值(唯一标识)
配置说明:
on
字段:触发CI的场景——当代码推送到main
分支,或有PR合并到main
时,自动运行;runs-on: ubuntu-latest
:使用GitHub提供的Ubuntu虚拟环境,无需自己搭建服务器;--junitxml=test-results.xml
:生成JUnit格式的测试报告,方便GitHub Actions展示测试结果;${{ github.sha }}
:GitHub的内置变量,代表当前提交的SHA哈希值,用于标记Docker镜像版本(避免重复)。拉取代码(Checkout):
使用actions/checkout@v4
动作,将GitHub仓库的代码复制到CI的虚拟环境中,是所有CI工作流的基础。
设置Python环境:
FastAPI依赖特定版本的Python(如3.9+),actions/setup-python@v5
会自动安装指定版本的Python,并配置pip
等工具。若不设置,虚拟环境的Python版本可能与项目不兼容(如默认Python 3.8,而项目用Python 3.11),导致依赖安装失败。
安装依赖:
通过pip install -r requirements.txt
安装项目所有依赖(包括FastAPI、Pydantic、Uvicorn等)。requirements.txt
需包含项目的所有第三方库及其版本(可通过pip freeze > requirements.txt
生成)。
运行测试:
使用pytest
运行测试用例(测试文件需以test_
开头,如test_main.py
)。--junitxml
参数生成的报告可在GitHub Actions的“Tests”标签中查看,方便快速定位失败的测试用例。
Allure是一款开源的测试报告框架,能生成美观、互动的可视化报告(支持图表、筛选、历史对比)。与传统的JUnit报告相比,Allure的优势:
FastAPI的测试通常使用pytest
和fastapi.TestClient
(模拟HTTP请求),而Allure通过pytest插件(allure-pytest
)集成到测试流程中。具体步骤:
allure-pytest
插件;@allure.feature
“功能模块”、@allure.story
“用户故事”);安装Java:Allure基于Java开发,需安装JRE 8+(如OpenJDK 11):
sudo apt install openjdk-11-jre
;brew install openjdk@11
;安装Allure CLI: 从Allure官网下载最新版本的CLI(github.com/allure-fram… PATH=$PATH:/path/to/allure-2.24.0/bin`)。
安装pytest插件:
pip install allure-pytest==2.13.2 # 最新版本(2024年3月)
在CI中安装Allure需添加以下步骤(修改.github/workflows/ci.yml
):
# 在“安装依赖”步骤后添加:
- name: Set up Java 11
uses: actions/setup-java@v4
with:
java-version: "11"
distribution: "temurin" # 使用Eclipse Temurin JDK(稳定且常用)
- name: Install Allure CLI
run: |
wget https://repo1.maven.org/maven2/io/qameta/allure/allure-commandline/2.24.0/allure-commandline-2.24.0.zip
unzip allure-commandline-2.24.0.zip
sudo mv allure-2.24.0 /opt/allure
sudo ln -s /opt/allure/bin/allure /usr/bin/allure # 添加到系统PATH
- name: Run tests with Allure
run: pytest --alluredir=allure-results # 生成Allure结果文件(存储在allure-results目录)
- name: Upload Allure results
uses: actions/upload-artifact@v4 # 将结果文件上传为GitHub artifact
with:
name: allure-results
path: allure-results
retention-days: 7 # 结果文件保留7天
以下是FastAPI的Allure测试用例示例(test_main.py
),覆盖GET /
和POST /items/
端点:
from fastapi.testclient import TestClient
from main import app # 导入FastAPI应用实例
import allure
# 初始化TestClient(模拟HTTP客户端,用于测试FastAPI端点)
client = TestClient(app)
# 定义Item模型(与main.py中的模型一致,确保测试数据符合约束)
class Item:
name: str
price: float
description: str | None = None
@allure.feature("Root Endpoint") # 功能模块:根路径端点
@allure.story("Get Root Message") # 用户故事:获取根路径的欢迎信息
@allure.title("Test root endpoint returns 200 OK") # 测试用例标题
@allure.severity(allure.severity_level.CRITICAL) # 优先级: critical(核心功能)
def test_read_root():
# 发送GET请求到根路径
response = client.get("/")
# 断言状态码为200
assert response.status_code == 200
# 断言响应内容符合预期
assert response.json() == {"message": "Hello FastAPI"}
@allure.feature("Item Management") # 功能模块:商品管理
@allure.story("Create New Item") # 用户故事:创建新商品
@allure.title("Test create item with valid data") # 测试用例标题
@allure.severity(allure.severity_level.NORMAL) # 优先级:normal(普通功能)
def test_create_item_valid_data():
# 构造符合Pydantic约束的请求数据(price>0)
item_data = {
"name": "iPhone 15",
"price": 9999.99,
"description": "A new smartphone"
}
# 发送POST请求到/items/端点(JSON格式)
response = client.post("/items/", json=item_data)
# 断言状态码为200
assert response.status_code == 200
# 断言响应内容包含提交的数据(假设id自增)
assert response.json() == {"id": 1, **item_data}
@allure.feature("Item Management")
@allure.story("Create New Item")
@allure.title("Test create item with invalid price") # 测试用例标题:无效价格(price≤0)
@allure.severity(allure.severity_level.MINOR) # 优先级:minor(次要功能)
def test_create_item_invalid_price():
# 构造无效数据(price=0,违反Pydantic的gt=0约束)
item_data = {
"name": "Fake Phone",
"price": 0,
"description": "Invalid price"
}
# 发送POST请求
response = client.post("/items/", json=item_data)
# 断言状态码为422(验证错误)
assert response.status_code == 422
本地环境生成报告:
运行测试用例并生成Allure结果文件:
pytest --alluredir=allure-results
启动Allure服务查看报告:
allure serve allure-results
浏览器会自动打开报告页面(如http://localhost:5050
),展示测试结果的统计信息(通过率、失败率)和详细的用例列表。
CI环境查看报告:
在GitHub Actions的“Artifacts”标签中下载allure-results
压缩包,解压后在本地运行allure serve allure-results
即可查看报告。
在FastAPI的CI流水线中,为什么需要设置Python环境?请结合示例说明其作用。
CI的虚拟环境是“干净”的(默认没有安装任何Python库),设置Python环境的作用是确保虚拟环境的Python版本与项目一致。例如:
actions/setup-python@v5
指定python-version: "3.11"
,可避免版本不兼容问题,确保依赖安装和测试正常运行。Allure的@allure.feature
和@allure.story
有什么区别?请用FastAPI的“商品管理”模块举例说明。
@allure.feature
:标记大的功能模块(如“商品管理”“用户管理”);@allure.story
:标记功能模块下的具体用户故事(如“创建商品”“删除商品”“查询商品列表”)。举例:
@allure.feature("商品管理") # 功能模块
@allure.story("创建商品") # 用户故事
def test_create_item():
# 测试用例代码
原因:
test_main.py
)与main.py
不在同一目录,导致Python无法找到main
模块;main.py
中未定义app
变量(app = FastAPI()
)。解决:
main.py
在同一目录;tests
目录下,需在conftest.py
中添加以下代码(将项目根目录加入Python路径):
import sys
sys.path.append(".") # 项目根目录(包含main.py的目录)
预防:
保持清晰的项目结构(如tests
目录存放测试文件,main.py
在根目录),并在pyproject.toml
中配置Pytest的根目录:
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]
原因:
test_
开头(如main_test.py
,Pytest默认不会识别);test_
开头(如create_item_test()
,Pytest不会执行);--alluredir
参数指定的目录错误(如allure-results
拼写错误)。解决:
test_
开头;pytest
命令的--alluredir
参数,确保目录名称正确(如allure-results
)。预防:
遵循Pytest的命名规范(测试文件test_*.py
、测试函数test_*
),并在pyproject.toml
中配置Pytest的测试文件匹配规则:
[tool.pytest.ini_options]
python_files = "test_*.py"
python_functions = "test_*"
原因:
请求数据违反Pydantic模型的约束(如price
字段小于等于0,或name
字段为空)。
解决:
response.json()
查看详细的错误信息(如"msg": "Input should be greater than 0"
)。示例:
若Item
模型的price
字段定义为price: float = Field(..., gt=0)
,则测试用例中的price
必须大于0:
# 错误数据(price=0)
item_data = {"name": "Fake Phone", "price": 0}
# 正确数据(price=999.99)
item_data = {"name": "iPhone 15", "price": 999.99}
预防:
在编写测试用例前,仔细阅读Pydantic模型的约束条件(如gt
、min_length
、regex
),确保测试数据符合要求。
283.16MB · 2025-09-16
286.59MB · 2025-09-16
286.72MB · 2025-09-16