返回首页

PHPUnit covers:开发者导航和测试覆盖

本文解释了如何在 PHPUnit 中使用 covers 注解和属性来改善测试与应用程序代码之间的导航。它涵盖了将测试链接到方法的方法、IDE 设置和覆盖限制。

改进 PHP 测试:PHPUnit 中 covers 指南
Advertisement 728x90

PHPUnit:掌握 Covers 提升测试导航与覆盖率

在 PHP 中测试 Web 控制器时,测试代码与应用代码之间的导航常常让人头疼。让我们来看看 PHPUnit 的 @covers 注解和属性如何解决这个问题,让开发更高效,同时不牺牲覆盖率。

测试与代码间的导航难题

通过 HTTP 请求测试控制器时,无法直接调用方法,这使得从测试跳转到代码变得很麻烦。例如,测试 /hello 端点的用例不会直接引用 GreetingController::hello(),只能手动在 IDE 中搜索,严重拖慢效率。

以下是一个控制器和测试的示例:

Google AdInline article slot
final class GreetingController extends AbstractController
{  
    #[Route('/hello')]
    public function hello(): Response
    {
        return new Response($this->generate("Hello!"));
    }
}
class GreetingControllerTest extends WebTestCase
{
    public function test_hello(): void
    {
        $content = get('/hello');
        assert_starts_with('Hello!', $content);
    }
}

三种链接测试与代码的方法

开发者常用各种技巧改善导航。

1. 使用反射的原生语法

将字符串路径替换为通过反射的方法调用,例如 request(GreetingController::hello(...))。缺点:测试会依赖应用代码,代码变更时容易出问题。

Google AdInline article slot

2. @see 注解

使用 PHPDoc 链接:

/**
 * @see \App\Controller\GreetingController::hello()
 */
public function test_hello(): void { ... }

优点:IDE 支持良好。缺点:需要完整类路径,通用标签无特殊处理。

Google AdInline article slot

3. PHPUnit 专属 Covers

专为覆盖率和导航设计的注解或属性。

在 PHPUnit 中使用 Covers 注解

PHPUnit 12 之前,@coversDefaultClass@covers 主宰一切:

/**
 * @coversDefaultClass \App\Controller\GreetingController
 */
class GreetingControllerTest extends WebTestCase
{
    /**
     * @covers ::hello
     */
    public function test_hello(): void { ... }
}

优势:

  • PhpStorm 支持一流:自动补全、导航、使用检测。
  • 支持自定义测试生成模板。

PhpStorm 模板的缺点:

  • 仅限两个 PHPUnit 模板。
  • 名称容易混淆。
  • 生成器偶尔出错。

转向 PHPUnit 12+ 的 Covers 属性

PHPUnit 12 弃用注解,转用属性,但有个坑:属性仅适用于类,不支持方法。

基本用法:

#[CoversClass('App\Controller\GreetingController')]
class GreetingControllerTest extends WebTestCase
{
    public function test_hello(): void { ... }
}

这会降低导航精度,因为缺少方法级指定。

使用 CoversMethod 指定方法:

#[CoversMethod('App\Controller\GreetingController', 'hello')]
#[CoversMethod('App\Controller\GreetingController', 'bye')]
class GreetingControllerTest extends WebTestCase
{
    public function test_hello(): void { ... }
    public function test_bye(): void { ... }
}

缺点:多方法测试时属性冗余。

最佳策略:一个方法一个测试类

每个方法专用测试类简化一切:

#[CoversMethod('App\Controller\GreetingController', 'hello')]
class HelloTest extends WebTestCase
{
    public function test_hello(): void { ... }
    public function test_hello2(): void { ... }
}

好处:

  • 减少认知负担。
  • 无需逐方法指定 covers。
  • 推动干净设计:一控制器一端点。

属性模板类似注解,但生成后需手动调整 CoversMethod。

IDE 集成与未来改进

PhpStorm 已支持通过 Ctrl+Shift+T 从测试跳转类,方法级导航即将到来。

关键要点

  • 使用 covers 提升导航,但注意覆盖率影响。
  • PHPUnit 12+ 属性仅限类级,需调整测试策略。
  • 理想做法:每方法一测试类,便于维护。
  • 配置 IDE 模板自动添加 covers。
  • 关注 PhpStorm 更新以获更好属性支持。

覆盖率限制:Covers 的最大缺陷

Covers 的核心作用是限定覆盖范围至类/方法。CoversClass 会排除外部代码——适合严格项目,但多数开发者只想找出空白。

禁用变通方法:

  • 极端黑客:测试前运行脚本剥离标签。
  • 实用修复:通过 Composer 打补丁 phpunit/php-code-coverage。

根据项目需求和团队偏好选择。

— Editorial Team

Advertisement 728x90

继续阅读