You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHPUnit测试用例中分组、复用与混合自定义断言类?

PHPUnit自定义断言的多类混合使用问题

现有实现方式

我目前通过继承PHPUnit默认的TestCase来编写自定义断言,分为两类:

1. 通用型断言

这类断言和业务领域无关,比如检查数组是否仅包含特定值的断言,实现如下:

<?php

namespace Kopernikus\TimrReportManager;

use PHPUnit\Framework\TestCase;

abstract class ArrayContainsValueTestCase extends TestCase
{
    public static function assertArrayOnlyContainsTrue(array $haystack): void
    {
        static::assertArrayOnlyContainsSameValue(true, $haystack);
    }

    public static function assertArrayOnlyContainsSameValue(mixed $expectedValue, array $haystack): void
    {
        $haystack = array_unique($haystack);
        static::assertTrue(static::areOnlySameValuesInArray($expectedValue, $haystack), message: 'The array contains of different values, yet sameness was expected');
    }

    private static function areOnlySameValuesInArray(mixed $expectedValue, array $haystack): bool
    {
        $haystack = array_unique($haystack);

        if (count($haystack) !== 1) {
            return false;
        }

        return reset($haystack) === $expectedValue;
    }

    public static function assertArrayOnlyContainsFalse(array $haystack)
    {
        static::assertArrayOnlyContainsSameValue(false, $haystack);

    }
}

2. 业务绑定型断言

这类断言和项目业务强关联,比如检查自定义TimeEntry值对象是否包含唯一指定工单ID的断言,独立成类实现:

<?php

namespace Kopernikus\TimrReportManager;

use Kopernikus\TimrReportManager\Dto\TimeEntry;
use PHPUnit\Framework\TestCase;

abstract class TimeEntryTestCase extends TestCase
{
    public static function assertTimeEntryHasTicketId(TimeEntry $timeEntry, int $ticketNumber)
    {
        $ticketNumberHashtag = '#' . (string)$ticketNumber;
        $count = substr_count($timeEntry->description, '#');
        static::assertSame(1, $count, 'the time entry must only contain one hashtag for the ticket id');
        static::assertSame($timeEntry->ticket, $ticketNumberHashtag);
    }
}

现有方案的痛点

当测试类MyTestThatRequiredBothAssertions需要同时使用这两类断言时,若通过继承链实现:

  • ArrayContainsValueTestCase extends TestCase
  • TimeEntryTestCase extends ArrayContainsValueTestCase
  • MyTestThatRequiredBothAssertions extends TimeEntryTestCase

这种方式存在明显问题:

  • 并非所有TimeEntryTestCase的子类都需要ArrayContainsValueTestCase的断言,强制继承会造成不必要的依赖
  • 后续新增更多自定义断言类时,继承树会越来越复杂混乱

我原本希望让两类断言类都直接继承TestCase,然后在测试类中同时使用,类似:

class MyTestThatRequiredBothAssertions extends ArrayContainsValueTestCase, TimeEntryTestCase

但PHP仅支持单继承,这种写法不可行。

需求与疑问

我需要一种能关注点分离的解决方案,满足以下要求:

  • 自定义断言拆分到多个独立文件中
  • 测试类可以自由混合使用不同的断言类,仅在需要时建立依赖
  • 保留IDE对断言方法的自动补全支持

我曾考虑过使用Trait,但Trait无法继承类,trait MyCustomAssertion extends TestCase这种写法不被允许。请问还有其他可行的解决方案吗?


内容的提问来源于stack exchange,提问作者k0pernikus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 22:16:14