Webman单元测试需先正确启动测试环境:在tests/bootstrap.php中显式加载框架上下文并初始化容器,避免“App not boot”错误;测试控制器时用Request::create()构造请求并手动注入依赖;对数据库、Redis等强依赖必须Mock;中间件测试应结合哑控制器验证响应状态码;测试方法命名须具业务描述性。

Webman 项目里写单元测试,不是加个 phpunit 就完事——关键得让测试真正跑得起来、改得动、查得清,尤其在控制器、中间件、服务类混用依赖注入的场景下,不隔离外部调用,测试一跑就卡在数据库或 HTTP 请求上。
Webman 中如何正确启动测试环境(避免 RuntimeException: App not boot)
Webman 启动依赖 bootstrap/app.php 和容器初始化,直接运行 phpunit 会因框架未加载而报错。必须显式引导应用实例,但又不能重复执行 bootstrap 全流程(否则路由/配置重复注册)。
- 在
tests/bootstrap.php中只加载核心容器和基础服务,跳过中间件和路由注册:$app = new Webman\App(__DIR__ . '/../config/'); $app->container->bind('config', new Webman\Config(__DIR__ . '/../config/')); // 不调用 $app->run() 或 $app->bootstrap() - 每个测试类用
setUp()创建轻量级Request/Response实例,而非复用全局$app;需要完整上下文时,用Webman\Test\HttpTestCase(若已封装)或手动模拟Container绑定 - 禁用自动加载日志、监控等生产级中间件:在测试配置中覆盖
config/middleware.php,返回空数组或仅保留Webman\Middleware\SessionMiddleware等必要项
测试控制器时怎么绕过路由匹配(避免 404 Not Found)
Webman 的控制器方法不是裸函数,它依赖 Request 对象和容器注入。直接 new 控制器并调用方法,$this->request 为 null,且无法解析 UserService 这类依赖。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 用
Webman\Http\Request::create()构造请求对象,再传入控制器实例:$request = Webman\Http\Request::create('/user/123', 'GET'); $controller = new UserController(); $result = $controller->show($request, '123'); - 若控制器构造函数含依赖(如
UserService $service),需手动注入:$service = $this->createMock(UserService::class); $controller = new UserController($service);
- 不要试图用
http://localhost/user/123发真实 HTTP 请求——这属于集成测试范畴,速度慢、不稳定,且与单元测试“隔离单个逻辑”的目标相悖
什么时候该用 Mock 而不是真实服务(警惕 DatabaseConnectionException)
只要测试类里出现 new Db()、Redis::get()、Http::get(),这个测试大概率会在 CI 环境失败,或污染本地数据库。
- 对数据访问层(如
UserModel),用createMock()替换整个模型类,然后 stub 查询结果:$model = $this->createMock(UserModel::class); $model->method('findById')->willReturn(['id' => 1, 'name' => 'test']); - 对框架工具类(如
Webman\Support\Db),优先 mock 其静态方法调用——PHPUnit 10+ 支持staticExpects(),但更稳妥的是将 DB 操作封装进服务类,再 mock 该服务 - 别 mock
Config::get():直接在测试前修改$app->config实例,或使用测试专用配置文件(如config/test/db.php)
如何验证中间件行为(比如权限校验是否拦截请求)
中间件本身不返回值,它的效果体现在 Response 状态码或响应体上。直接测中间件类,容易陷入“调了但没生效”的盲区。
- 把中间件和控制器链起来测:构造一个最小路由,绑定目标中间件和哑控制器,检查最终响应:
$request = Request::create('/admin', 'GET'); $response = (new AuthMiddleware())->process($request, function ($req) { return Response::ok('success'); }); $this->assertEquals(401, $response->getStatusCode()); - 若中间件依赖
Session或Token,mock 对应的解析逻辑,而不是启动完整 session 流程 - 避免在中间件测试中 assert 日志内容——日志是副作用,应通过响应状态或抛出异常来验证主逻辑
最常被跳过的一步:没给测试方法起描述性名字。像 testUserShow() 不如 testShowReturns404WhenUserNotFound() ——后者在 CI 报错时能立刻定位是哪个业务分支崩了,而不是打开测试文件逐行读逻辑。

















