Gitea 测试体系详解:四类自动化测试的本地运行、数据库配置与源码级实现剖析
Gitea 的自动化测试由四部分组成:后端单元测试(unit tests)、集成测试(integration tests)、端到端测试(e2e tests)和迁移测试(migration tests)。本篇基于仓库中 testing.md 的完整内容展开,并结合 Makefile、tools/test-integration.sh、tools/test-e2e.sh 及 CI 工作流源码,逐条说明每类测试的运行命令、环境变量、单测选择器与底层实现机制。读完本文,你可以零外部依赖地在本地跑通 Gitea 全量测试,掌握把集成测试指向 MySQL/PostgreSQL/MSSQL 的方法,并能理解测试框架在数据库重置、分片执行、Playwright 隔离实例等方面的设计原理。
本地运行默认使用 SQLite,无需额外服务即可开始测试。构建环境的前置依赖参见 build-setup.md,构建工作流参见 development.md。
一、后端单元测试(Unit Tests)
1.1 测试位置与运行方式
Gitea 的后端单元测试以 *_test.go 文件的形式放在被测代码旁边。仓库中这类文件遍布 models/、modules/、services/、routers/ 等目录。运行方式为:
make test-backend
从 Makefile 可以看到,test-backend 本质上是:
test-backend: ## test backend files
@echo "Running go test with $(GOTEST_FLAGS) -tags '$(TAGS)'..."
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' $(GO_TEST_PACKAGES)
也就是说,GOTEST_FLAGS(如 -race -timeout=20m)和 TAGS(如 bindata、gogit)都通过环境变量注入,CI 中正是这样使用的——见 pull-db-tests.yml 的 test-unit 任务:
- name: unit-tests
run: make test-backend
env:
GOTEST_FLAGS: -race -timeout=20m
TAGS: bindata
1.2 SQL 日志:GITEA_TEST_LOG_SQL
设置 GITEA_TEST_LOG_SQL=1 可以输出测试执行过程中的全部 SQL 语句,对排查 ORM 映射问题非常有用。该开关的实现位于 testdb.go:
switch os.Getenv("GITEA_TEST_LOG_SQL") {
从源码结构看,该环境变量在测试数据库初始化时被读取,用于切换 SQL 日志级别。
1.3 运行单个后端测试
有两种方式:直接使用 go test -run,或使用 Makefile 的 # 选择器:
go test -run '^TestName$' ./modulepath/
make test-backend#TestName
# 选择器对应 Makefile 中的 pattern rule:
.PHONY: test-backend\#%
test-backend\#%:
@echo "Running go test with -tags '$(TAGS)'..."
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES)
$(subst .,/,$*) 这一细节值得注意:它允许用 . 分隔包路径与测试名(例如 make test-backend#models/db.TestXxx),Makefile 会自动将其转换为 -run 所需的 / 形式。
另外,CI 中在单测结束后还会执行 make test-check(Makefile),通过 git status -s 检查测试是否污染了源码树,要求测试产物必须写入临时目录。
1.4 前端单元测试(Vitest)
前端单元测试使用 Vitest 浏览器模式运行:
make test-frontend
# single file:
pnpm exec vitest <path-filter>
对应 Makefile:
.PHONY: test-frontend
test-frontend: playwright ## test frontend files
pnpm exec vitest
注意它依赖 playwright 目标(会执行 tools/playwright.sh 安装浏览器),这也解释了为何 Vitest 能以浏览器模式运行。前端源码位于 web_src/js/,测试文件为 *.test.ts / *.test.tsx。
二、集成测试(Integration Tests)
2.1 基本运行
集成测试让 Gitea 连接真实数据库运行,测试代码位于 tests/integration/(当前仓库约有 300 余个 *_test.go 文件),并且要求安装 Git LFS。数据库由 GITEA_TEST_DATABASE 选择,留空时默认 SQLite,无需任何外部服务:
make test-integration
运行单个集成测试同样使用 # 选择器:
make test-integration#TestName
如果遇到数据库版本不匹配、SSH push 失败之类的报错,先做一次干净重建:
make clean build
2.2 Makefile 的编译-执行两阶段
test-integration 目标与 test-backend 不同,它采用「先编译 .test 二进制、再运行脚本」的两阶段结构(Makefile):
.PHONY: test-integration
test-integration: $(EXECUTABLE)
@# Use a compiled binary: testlogger forwards gitea logs to t.Log, so `go test -v`
@# would flood output per passing test. testcache can't help these tests anyway —
@# they mutate the work directory, so cache inputs change between runs.
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c gitea.dev/tests/integration -o ./test-integration-$(GITEA_TEST_DATABASE).test
./tools/test-integration.sh ./test-integration-$(GITEA_TEST_DATABASE).test
编译产物名为 test-integration-$(GITEA_TEST_DATABASE).test——不同数据库使用独立二进制,避免切换数据库时残留旧的测试环境。注释中说明了这样做的两个原因:测试框架的 testlogger 会把 Gitea 日志转发到 t.Log,直接 go test -v 会导致输出泛滥;且集成测试会修改工作目录,Go 的测试缓存无法生效。
运行脚本 test-integration.sh 还实现了 CI 的测试分片能力:当设置 TEST_SHARD / TEST_TOTAL_SHARDS 时,脚本先用 -test.list 枚举全部顶层测试,按 (NR-1) % total == shard-1 取模切分到当前分片,再用 -test.run 只执行本分片的测试集合:
NAMES=$("$BINARY" -test.list='^Test' | LC_ALL=C sort -u | awk -v r=$((TEST_SHARD - 1)) -v t="$TEST_TOTAL_SHARDS" '(NR - 1) % t == r')
...
exec "$BINARY" -test.run "^($PATTERN)\$"
这解释了 CI 中 PostgreSQL 被拆成两个 shard 并行跑(见 pull-db-tests.yml 的 test-pgsql-shard-1/2 与 run-migration: "true" 的分工)。
2.3 测试环境的初始化流程
集成测试的统一入口是 test_utils.go 中的 InitIntegrationTest(),其调用链清晰展示了测试环境如何从零搭起:
func InitIntegrationTest() error {
testlogger.Init()
err := setting.PrepareIntegrationTestConfig() // 按 GITEA_TEST_DATABASE 生成测试配置
...
setting.SetupGiteaTestEnv()
git.InitFull()
setting.LoadDBSetting()
cleanupDb, err := unittest.ResetTestDatabase() // 重置数据库并导入 fixtures
...
storage.Init()
routers.InitWebInstalled(graceful.GetManager().HammerContext())
return nil
}
同文件还提供了一组资源准备函数,例如 PrepareGitRepoDirectory(把 tests/gitea-repositories-meta/ 中预置的仓库元数据同步到测试仓库根目录)、PrepareLFSStorage(加载 tests/gitea-lfs-meta/ 的 LFS fixture)、PrepareAttachmentsStorage 与 PrepareCleanPackageData。这些函数解释了为何 Git LFS 是集成测试的硬性依赖——仓库对象本身就是通过 LFS 存储和分发的。
2.4 使用其他数据库运行
设置 GITEA_TEST_DATABASE 与配套的 TEST_* 连接变量即可切换目标数据库。以下命令启动一次性数据库容器(Ctrl-C 停止并删除),然后对其运行测试。
MySQL
docker run -e "MYSQL_DATABASE=test" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -p 3306:3306 --rm --name mysql mysql:latest
GITEA_TEST_DATABASE=mysql TEST_MYSQL_HOST=localhost:3306 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=root TEST_MYSQL_PASSWORD='' make test-integration
PostgreSQL(另需一个 MinIO 容器提供对象存储)
docker run -e "POSTGRES_DB=test" -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=postgres" -p 5432:5432 --rm --name pgsql postgres:latest
docker run --rm -p 9000:9000 -e MINIO_ROOT_USER=123456 -e MINIO_ROOT_PASSWORD=12345678 --name minio bitnamilegacy/minio:2023.8.31
GITEA_TEST_DATABASE=pgsql TEST_MINIO_ENDPOINT=localhost:9000 TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=postgres TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-integration
MSSQL
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest
GITEA_TEST_DATABASE=mssql TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-integration
各数据库对应的测试配置模板位于 tests/ 目录下(mysql.ini.tmpl、pgsql.ini.tmpl、mssql.ini.tmpl、sqlite.ini.tmpl)。从源码结构看,TEST_MINIO_ENDPOINT 等变量会被注入对应模板,生成隔离的测试 app.ini。
三、用 Gitea Runner 本地运行 CI 数据库工作流
CI 的数据库测试任务可以用 Gitea Runner 在本地复现。全量运行所有任务资源开销较大,官方不建议这么做:
gitea-runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest
先列出可用 job 名,再单独运行其中一个:
gitea-runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -l
gitea-runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -j <job_name>
被引用的工作流文件即 pull-db-tests.yml,其中定义了 test-pgsql-shard-1/2、test-sqlite、test-unit、test-mysql、test-mssql 等任务;每个数据库任务在跑 make test-integration 之前都会先执行 GITEA_TEST_DATABASE=<db> make test-migration,即 CI 中集成测试与迁移测试是串行的。
四、端到端测试(E2E Tests)
E2E 测试使用 Playwright 驱动一个真实运行的 Gitea 实例:
make test-e2e
运行单个 e2e 测试文件时通过 GITEA_TEST_E2E_FLAGS 传入:
GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e
常用环境变量:
| 变量 | 说明 |
|---|---|
GITEA_TEST_E2E_DEBUG |
设置后显示 Gitea 服务端输出 |
GITEA_TEST_E2E_FLAGS |
透传给 Playwright 的额外参数,如 --ui |
GITEA_TEST_E2E_TIMEOUT_FACTOR |
超时倍数(CI 上默认 4,本地默认 1) |
这些行为都能在 test-e2e.sh 中找到实现依据,该脚本是 make test-e2e 真正的执行体(Makefile 仅转发调用):
- Playwright 运行模式探测:
detect_playwright_mode在 Linux 上检查/etc/os-release,只有 Ubuntu/Debian 才允许本地模式(Playwright 官方只支持这两个发行版),否则自动切换为container模式,拉取与package.json中@playwright/test版本精确匹配的mcr.microsoft.com/playwright:v<ver>-noble镜像并运行playwright run-server; - 隔离的 Gitea 实例:脚本用
mktemp -d创建一次性工作目录,生成一份内嵌app.ini——SQLite 数据库、随机空闲端口、INSTALL_LOCK = true、关闭验证码,并把GITEA_TEST_E2E=true注入环境变量,然后启动编译好的 Gitea 二进制,最长等待 120 秒直到服务可达; - 测试账号:通过
./gitea admin user create命令行创建一个管理员账号e2e-admin(密码password,域名e2e.gitea.com),再以GITEA_TEST_E2E_URL、GITEA_TEST_E2E_USER等环境变量导出给 Playwright; - 超时因子:脚本中明确注释了因子语义——CI 慢机器上用 4 倍超时,本地(如 MacBook Pro M1+)用 1 倍,与文档表格中的默认值完全一致(test-e2e.sh);
- 收尾:最后执行
pnpm exec playwright test "$@",GITEA_TEST_E2E_FLAGS中的参数就是这里的"$@"。
E2E 测试用例文件位于 tests/e2e/,覆盖登录、注册、Pull Request 创建与评审、Issue、组织、release 等约 30 个场景(如 pr-create.test.ts、login.test.ts)。CI 侧对应 pull-e2e-tests.yml,在 make frontend && make backend && make playwright 之后执行 make test-e2e 并开启 GITEA_TEST_E2E_DEBUG: 1。
五、迁移测试(Migration Tests)
只要修改了 models/ 下持久化到数据库的结构体,通常就需要在 modelmigration/ 中新增一个迁移。运行迁移测试:
make test-migration
该目标由两部分组成(Makefile):
.PHONY: test-migration
test-migration: migrations.integration.test migrations.individual.test
.PHONY: migrations.integration.test
migrations.integration.test:
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' gitea.dev/tests/integration/migration-test
.PHONY: migrations.individual.test
migrations.individual.test:
@# tests of multiple packages use the same database, don't run in parallel
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
-p 1 的注释点明了原因:各迁移包的测试共用同一个数据库,不能并行。此外 Makefile 还提供了 migrations.individual.test#<package> 选择器,可单独跑某个版本目录(如 v1_26/)下的迁移测试。迁移测试以 fixture(YAML 描述的数据库快照,如 fixtures/ 下各测试目录)作为输入,验证迁移前后的表结构一致性;版本号计算逻辑(calcDBVersion、ExpectedDBVersion)可在 migrations_test.go 中查看。
六、持续集成与测试编写建议
CI 的职责边界(与 pull-db-tests.yml 的实际任务一一对应):
- 单元测试:
test-unit任务带-race与 20 分钟超时,分别以bindata和bindata gogit两套 build tags 各跑一遍(gogit 模式下跳过依赖外部网络的用例,GITEA_TEST_CI_SKIP_EXTERNAL: true); - 集成测试:对每种受支持的数据库(sqlite、mysql、pgsql、mssql)各跑一轮集成测试,其中 PostgreSQL 额外用 OpenLDAP 与 MinIO 服务容器,MySQL 额外挂了 Elasticsearch 与 SMTP/IMAP mock 服务(
TEST_INDEXER_CODE_ES_URL等环境变量即由此注入); - 迁移测试:每个数据库任务都先执行
make test-migration,验证从近期多个 Gitea 版本向当前版本迁移的完整性; - E2E 测试:独立工作流 pull-e2e-tests.yml 在前后端变更时触发,超时预算 10 分钟。
对贡献者的官方建议(原文档原话整理):提交 PR 时应按情况补充相应的单元测试与集成测试;能在隔离条件下测试的逻辑优先写单元测试;保持本地集成与 e2e 测试足够快,目标是单次运行不超过 2 秒。
小结:命令速查
| 场景 | 命令 |
|---|---|
| 后端单元测试(全量) | make test-backend |
| 后端单元测试(单个) | make test-backend#TestName 或 go test -run '^TestName$' ./modulepath/ |
| 前端单元测试 | make test-frontend / pnpm exec vitest <path-filter> |
| 集成测试(SQLite 默认) | make test-integration |
| 集成测试(单个) | make test-integration#TestName |
| 集成测试(MySQL/pgsql/mssql) | 加 GITEA_TEST_DATABASE=... 与 TEST_* 变量 |
| E2E 测试 | make test-e2e,单文件用 GITEA_TEST_E2E_FLAGS='<filepath>' |
| 迁移测试 | make test-migration,单包用 make migrations.individual.test#<pkg> |
| 报错时先干净重建 | make clean build |
整个测试体系的设计特点是:本地零依赖起步(SQLite + 无外部服务),逐级扩展到真实数据库、隔离 Playwright 实例和迁移 fixture 验证;所有 make 目标最终都可追溯到 Makefile 与 tools/ 下的两个 shell 脚本,行为透明、可按需裁剪。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00