boxmoe_header_banner_img

Hello! 欢迎来到悠悠畅享网!

文章导读

如何解决PHPUnit测试中数据提供者重复冗余的问题,使用ergebnis/data-provider让你的测试代码更优雅高效


avatar
作者 2025年8月27日 13

可以通过一下地址学习composer学习地址

作为一名php开发者,我们深知单元测试的重要性。而phpunit作为事实上的标准测试框架,其数据提供者(data provider)功能更是让我们能够用一套测试逻辑覆盖多种输入场景,极大地提高了测试效率。然而,在实际项目中,我却常常被一个问题困扰:为各种常见数据类型(比如空字符串、空白字符串、负数、零、布尔值等)编写数据提供者时,总是不得不一遍又一遍地重复编写相似的代码。

想象一下,你正在测试一个用户注册功能,需要验证用户名不能是空字符串或只包含空格。你可能会写出这样的代码:

<pre class="brush:php;toolbar:false;"><?php  // ... 其他代码 ...  class UserTest extends PHPUnitFrameworkTestCase {     // ... 其他测试方法 ...      /**      * @dataProvider provideInvalidUserNames      */     public function testUserNameCannotBeEmptyOrBlank(string $value): void     {         $this->expectException(InvalidArgumentException::class);         $this->expectExceptionMessage('Value can not be an empty or blank string.');          UserName::fromString($value);     }      public static function provideInvalidUserNames(): array     {         return [             'empty string' => [''],             'blank string with space' => [' '],             'blank string with tab' => ["t"],             'blank string with newline' => ["n"],         ];     } }  // 假设有一个这样的 UserName 类 final class UserName {     private string $value;      private function __construct(string $value)     {         $this->value = $value;     }      public static function fromString(string $value): self     {         if (trim($value) === '') {             throw new InvalidArgumentException('Value can not be an empty or blank string.');         }          return new self($value);     } }

这看起来没问题,对吧?但如果你的项目中还有十几个、几十个地方需要验证“非空非空白字符串”的逻辑呢?你就会发现自己不得不复制粘贴,或者为每个模块都创建一套几乎相同的数据提供者。这不仅导致了大量的样板代码,让测试文件变得臃肿不堪,而且一旦需要调整某个“空白字符串”的定义(比如增加一个Unicode空白字符),你就得在所有地方手动修改,维护成本极高,也容易出错。这种重复劳动,简直是开发者的噩梦!

正当我为此头疼不已时,我偶然发现了

ergebnis/data-provider

这个 composer 包。它就像一道曙光,彻底解决了我在 PHPUnit 测试中数据提供者重复冗余的问题,让我的测试代码变得前所未有的优雅和高效。

ergebnis/data-provider

提供了一系列预定义的、通用的数据提供者,涵盖了 PHP 中最常用的数据类型,例如布尔值、浮点数、整数、NULL对象、资源、字符串以及 UUID。这意味着,你不再需要为这些常见类型的数据编写自己的数据提供者,只需简单引用即可。

立即学习PHP免费学习笔记(深入)”;

使用

ergebnis/data-provider

非常简单,首先通过 Composer 安装:

<pre class="brush:php;toolbar:false;">composer require ergebnis/data-provider

安装完成后,你就可以在你的 PHPUnit 测试中直接引用它提供的各种数据提供者了。让我们回到之前那个“用户名不能是空字符串或空白字符串”的例子,使用

ergebnis/data-provider

后,代码会变得异常简洁:

<pre class="brush:php;toolbar:false;"><?php  declare(strict_types=1);  namespace ExampleTest;  use ErgebnisDataProvider; use PHPUnitFramework; use InvalidArgumentException; // 假设 UserName::fromString 抛出此异常  // 假设有一个这样的 UserName 类 final class UserName {     private string $value;      private function __construct(string $value)     {         $this->value = $value;     }      public static function fromString(string $value): self     {         if (trim($value) === '') {             throw new InvalidArgumentException('Value can not be an empty or blank string.');         }          return new self($value);     } }  final class ExampleTest extends FrameworkTestCase {     /**      * @dataProvider ErgebnisDataProviderStringProvider::blank()      * @dataProvider ErgebnisDataProviderStringProvider::empty()      */     public function testFromNameRejectsInvalidValueWithAnnotation(string $value): void     {         $this->expectException(InvalidArgumentException::class);         $this->expectExceptionMessage('Value can not be an empty or blank string.');          UserName::fromString($value);     }      // 如果你使用 PHP 8+ 的 Attribute,也可以这样写:     #[FrameworkDataProviderExternal(DataProviderStringProvider::class, 'blank')]     #[FrameworkDataProviderExternal(DataProviderStringProvider::class, 'empty')]     public function testFromNameRejectsInvalidValueWithAttribute(string $value): void     {         $this->expectException(InvalidArgumentException::class);         $this->expectExceptionMessage('Value can not be an empty or blank string.');          UserName::fromString($value);     } }

看到了吗?我们完全移除了

provideInvalidUserNames()

这个方法!取而代之的是直接引用

ErgebnisDataProviderStringProvider

中的

blank()

empty()

方法。这两个方法分别提供了只包含空白字符的字符串和空字符串作为测试数据。代码瞬间变得清晰、精简,且易于理解。

ergebnis/data-provider

提供了非常丰富的通用数据提供者,涵盖了你可能遇到的大部分场景:

  • ErgebnisDataProviderBoolProvider

    : 提供

    true

    false

    或任意布尔值(

    arbitrary()

    )。

  • ErgebnisDataProviderFloatProvider

    : 提供任意浮点数(

    arbitrary()

    )、大于1(

    greaterThanOne()

    )、大于0(

    greaterThanZero()

    )、小于1(

    lessThanOne()

    )、小于0(

    lessThanZero()

    )、1.0(

    one()

    )、0.0(

    zero()

    )等。

  • ErgebnisDataProviderIntProvider

    : 提供任意整数(

    arbitrary()

    )、大于1(

    greaterThanOne()

    )、大于0(

    greaterThanZero()

    )、小于1(

    lessThanOne()

    )、小于0(

    lessThanZero()

    )、1(

    one()

    )、0(

    zero()

    )等。

  • ErgebnisDataProviderNullProvider

    : 专门提供

    null

    null()

    )。

  • ErgebnisDataProviderObjectProvider

    : 提供

    stdClass

    实例(

    object()

    )。

  • ErgebnisDataProviderResourceProvider

    : 提供一个资源类型(

    resource()

    )。

  • ErgebnisDataProviderStringProvider

    : 除了

    blank()

    empty()

    ,还有

    arbitrary()

    (任意字符串),

    trimmed()

    (无前后空格的非空字符串),

    untrimmed()

    (带前后空格的非空字符串),

    withWhitespace()

    (包含空格的字符串) 等。

  • ErgebnisDataProviderUuidProvider

    : 提供小写(

    caseLower()

    )或大写(

    caseUpper()

    )的 UUID 字符串,以及任意大小写(

    arbitrary()

    )。

这些提供者都设计得非常细致,几乎覆盖了你在单元测试中所有常见的输入场景。

引入

ergebnis/data-provider

后,我真切感受到了它的巨大优势:

  1. 告别样板代码:彻底消除了为通用数据类型重复编写数据提供者的烦恼,让我的测试文件更专注于业务逻辑的验证。
  2. 提升可读性:测试方法不再被冗长的数据提供者方法所干扰,一眼就能看出测试的意图。
  3. 增强一致性:所有团队成员都可以使用同一套标准化的数据,确保测试覆盖的全面性和一致性。
  4. 简化维护:如果需要更新某种数据类型(例如,增加一种新的空白字符),只需
    ergebnis/data-provider

    库自身更新,或者我可以在我自己的项目里扩展它,而无需修改大量测试文件。

  5. 提高开发效率:编写测试的速度显著加快,因为大部分基础数据我已经无需自己去构建。
  6. 强大的组合能力:你可以像例子中那样,通过
    @dataProvider

    #[FrameworkDataProviderExternal]

    组合多个提供者,轻松构建复杂的测试场景。

总之,

ergebnis/data-provider

是一个非常实用且优雅的 PHPUnit 辅助库。如果你也曾被数据提供者的重复编写所困扰,那么我强烈推荐你尝试一下它。它不仅能让你的测试代码更加精简、易读,还能大幅提升你的开发效率和测试质量。让我们的测试代码,从今天开始,变得更加智能和高效吧!

以上就是如何解决PHPUnit测试中数据提供者重复冗余的问题,使用ergebnis/data-provider让你的测试代码更优雅高效的详细内容,更多请关注



评论(已关闭)

评论已关闭