首页
/ Gitea 测试体系详解:四类自动化测试的本地运行、数据库配置与源码级实现剖析

Gitea 测试体系详解:四类自动化测试的本地运行、数据库配置与源码级实现剖析

2026-09-05 20:06:53作者:史锋燃Gardner

Gitea 的自动化测试由四部分组成:后端单元测试(unit tests)、集成测试(integration tests)、端到端测试(e2e tests)和迁移测试(migration tests)。本篇基于仓库中 testing.md 的完整内容展开,并结合 Makefiletools/test-integration.shtools/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(如 bindatagogit)都通过环境变量注入,CI 中正是这样使用的——见 pull-db-tests.ymltest-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-checkMakefile),通过 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.ymltest-pgsql-shard-1/2run-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)、PrepareAttachmentsStoragePrepareCleanPackageData。这些函数解释了为何 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.tmplpgsql.ini.tmplmssql.ini.tmplsqlite.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/2test-sqlitetest-unittest-mysqltest-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 仅转发调用):

  1. 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
  2. 隔离的 Gitea 实例:脚本用 mktemp -d 创建一次性工作目录,生成一份内嵌 app.ini——SQLite 数据库、随机空闲端口、INSTALL_LOCK = true、关闭验证码,并把 GITEA_TEST_E2E=true 注入环境变量,然后启动编译好的 Gitea 二进制,最长等待 120 秒直到服务可达;
  3. 测试账号:通过 ./gitea admin user create 命令行创建一个管理员账号 e2e-admin(密码 password,域名 e2e.gitea.com),再以 GITEA_TEST_E2E_URLGITEA_TEST_E2E_USER 等环境变量导出给 Playwright;
  4. 超时因子:脚本中明确注释了因子语义——CI 慢机器上用 4 倍超时,本地(如 MacBook Pro M1+)用 1 倍,与文档表格中的默认值完全一致(test-e2e.sh);
  5. 收尾:最后执行 pnpm exec playwright test "$@"GITEA_TEST_E2E_FLAGS 中的参数就是这里的 "$@"

E2E 测试用例文件位于 tests/e2e/,覆盖登录、注册、Pull Request 创建与评审、Issue、组织、release 等约 30 个场景(如 pr-create.test.tslogin.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/ 下各测试目录)作为输入,验证迁移前后的表结构一致性;版本号计算逻辑(calcDBVersionExpectedDBVersion)可在 migrations_test.go 中查看。

六、持续集成与测试编写建议

CI 的职责边界(与 pull-db-tests.yml 的实际任务一一对应):

  • 单元测试test-unit 任务带 -race 与 20 分钟超时,分别以 bindatabindata 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#TestNamego 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 脚本,行为透明、可按需裁剪。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384