CI中composer test失败主因是默认执行composer install --no-dev导致autoload-dev类不可用,且PHP扩展缺失或autoload配置不全;需显式安装PHP及扩展、验证autoload、统一使用composer test脚本而非直接调用phpunit。

直接运行 composer test 就能跑测试,但 CI 里失败八成不是 PHPUnit 写错了,而是环境、加载或命令调用方式不对。
为什么本地 composer test 能过,CI 却报 Class not found
根本原因:CI 默认执行 composer install --no-dev(为安全和体积),而你的测试类在 autoload-dev 里,但主命名空间没进 autoload —— 导致 MyPackageSomething 这类生产类都加载不了。
- 检查
composer.json的autoload是否覆盖了src/,例如:"autoload": { "psr-4": { "MyPackage\": "src/" } } -
autoload-dev只用于测试辅助类(如tests/Support/TestCase.php),别让它被生产代码require - CI 中加一行验证:
composer dump-autoload --no-dev && php -r "echo class_exists('MyPackage\Something') ? 'ok' : 'fail';" - 别信
vendor/autoload.php一定可用——CI runner 的 PHP 扩展(mbstring、xml)可能缺失,phpunit启动就挂
GitHub Actions 里怎么写才不踩坑
别抄模板,重点是控制变量:PHP 版本、扩展、依赖安装方式、命令入口。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 用
shivammathur/setup-php@v2,显式启用关键扩展:extensions: mbstring, xml, ctype, json, openssl - PHP 版本必须矩阵化:
php-version: [8.1, 8.2, 8.3],别写死'8.2',否则主版本升级后全红 - 永远用
composer install(不是update),并前置校验:composer validate --no-check-publish - 测试命令写
composer test,而不是vendor/bin/phpunit;确保composer.json里"test": "phpunit --no-coverage --fail-on-warning" - 缓存只设
~/.composer/cache,别缓存vendor/——不同 PHP 版本下 opcache 编译产物不兼容,会随机 autoload 失败
composer test 脚本里该塞什么
脚本不是摆设,它是统一本地与 CI 行为的唯一契约。硬编码路径、忽略警告、跳过静态分析,都会让 CI 成为“盲测”。
- 基础必须项:
"test": "phpunit --no-coverage --fail-on-warning"(--fail-on-warning把 deprecation 当失败,防隐性腐化) - 加 PHPStan:
"test:static": "phpstan analyse --level=8 src/ tests/",CI 里连用:composer test && composer test:static - 别在脚本里写
./vendor/bin/phpunit——Composer 的 bin 自动发现机制会根据当前环境选对二进制 - 需要传参时用双横线透传:
composer test -- --filter=CalculatorTest,PHPUnit 才能收到 - 如果用了
phpunit.xml.dist,确保bootstrap="vendor/autoload.php",且该文件真实存在
最常被忽略的一点:CI 不是“跑通就行”,而是要暴露问题。比如 composer.lock 被改了却没触发测试,或者 phpunit 因缺少 ext-xml 静默退出——这些都得靠显式声明和前置校验来兜底,不能靠运气。

















