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

WBOY
发布: 2025-08-27 13:10:43
原创
448人浏览过

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

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

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

<pre class="brush:php;toolbar:false;"><?php

// ... 其他代码 ...

class UserTest extends \PHPUnit\Framework\TestCase
{
    // ... 其他测试方法 ...

    /**
     * @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 安装:

SpeakingPass-打造你的专属雅思口语语料
SpeakingPass-打造你的专属雅思口语语料

使用chatGPT帮你快速备考雅思口语,提升分数

SpeakingPass-打造你的专属雅思口语语料 25
查看详情 SpeakingPass-打造你的专属雅思口语语料
<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 Example\Test;

use Ergebnis\DataProvider;
use PHPUnit\Framework;
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 Framework\TestCase
{
    /**
     * @dataProvider \Ergebnis\DataProvider\StringProvider::blank()
     * @dataProvider \Ergebnis\DataProvider\StringProvider::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,也可以这样写:
    #[Framework\DataProviderExternal(DataProvider\StringProvider::class, 'blank')]
    #[Framework\DataProviderExternal(DataProvider\StringProvider::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()
登录后复制
这个方法!取而代之的是直接引用
Ergebnis\DataProvider\StringProvider
登录后复制
中的
blank()
登录后复制
empty()
登录后复制
方法。这两个方法分别提供了只包含空白字符的字符串和空字符串作为测试数据。代码瞬间变得清晰、精简,且易于理解。

ergebnis/data-provider
登录后复制
提供了非常丰富的通用数据提供者,涵盖了你可能遇到的大部分场景:

  • Ergebnis\DataProvider\BoolProvider
    登录后复制
    : 提供
    true
    登录后复制
    false
    登录后复制
    或任意布尔值(
    arbitrary()
    登录后复制
    )。
  • Ergebnis\DataProvider\FloatProvider
    登录后复制
    : 提供任意浮点数(
    arbitrary()
    登录后复制
    )、大于1(
    greaterThanOne()
    登录后复制
    )、大于0(
    greaterThanZero()
    登录后复制
    )、小于1(
    lessThanOne()
    登录后复制
    )、小于0(
    lessThanZero()
    登录后复制
    )、1.0(
    one()
    登录后复制
    )、0.0(
    zero()
    登录后复制
    )等。
  • Ergebnis\DataProvider\IntProvider
    登录后复制
    : 提供任意整数(
    arbitrary()
    登录后复制
    )、大于1(
    greaterThanOne()
    登录后复制
    )、大于0(
    greaterThanZero()
    登录后复制
    )、小于1(
    lessThanOne()
    登录后复制
    )、小于0(
    lessThanZero()
    登录后复制
    )、1(
    one()
    登录后复制
    )、0(
    zero()
    登录后复制
    )等。
  • Ergebnis\DataProvider\NullProvider
    登录后复制
    : 专门提供
    null
    登录后复制
    null()
    登录后复制
    )。
  • Ergebnis\DataProvider\ObjectProvider
    登录后复制
    : 提供
    stdClass
    登录后复制
    实例(
    object()
    登录后复制
    )。
  • Ergebnis\DataProvider\ResourceProvider
    登录后复制
    : 提供一个资源类型(
    resource()
    登录后复制
    )。
  • Ergebnis\DataProvider\StringProvider
    登录后复制
    : 除了
    blank()
    登录后复制
    empty()
    登录后复制
    ,还有
    arbitrary()
    登录后复制
    (任意字符串),
    trimmed()
    登录后复制
    (无前后空格的非空字符串),
    untrimmed()
    登录后复制
    (带前后空格的非空字符串),
    withWhitespace()
    登录后复制
    (包含空格的字符串) 等。
  • Ergebnis\DataProvider\UuidProvider
    登录后复制
    : 提供小写(
    caseLower()
    登录后复制
    )或大写(
    caseUpper()
    登录后复制
    )的 UUID 字符串,以及任意大小写(
    arbitrary()
    登录后复制
    )。

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

引入

ergebnis/data-provider
登录后复制
后,我真切感受到了它的巨大优势:

  1. 告别样板代码:彻底消除了为通用数据类型重复编写数据提供者的烦恼,让我的测试文件更专注于业务逻辑的验证。
  2. 提升可读性:测试方法不再被冗长的数据提供者方法所干扰,一眼就能看出测试的意图。
  3. 增强一致性:所有团队成员都可以使用同一套标准化的数据,确保测试覆盖的全面性和一致性。
  4. 简化维护:如果需要更新某种数据类型(例如,增加一种新的空白字符),只需
    ergebnis/data-provider
    登录后复制
    库自身更新,或者我可以在我自己的项目里扩展它,而无需修改大量测试文件。
  5. 提高开发效率:编写测试的速度显著加快,因为大部分基础数据我已经无需自己去构建。
  6. 强大的组合能力:你可以像例子中那样,通过
    @dataProvider
    登录后复制
    #[Framework\DataProviderExternal]
    登录后复制
    组合多个提供者,轻松构建复杂的测试场景。

总之,

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

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

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号