在命名空间中组织 PHPUnit 测试 [关闭]
Posted
技术标签:
【中文标题】在命名空间中组织 PHPUnit 测试 [关闭]【英文标题】:Organizing PHPUnit Tests in Namespaces [closed] 【发布时间】:2012-08-20 11:33:23 【问题描述】:我看到了将 phpUnit 单元测试组织到命名空间层次结构中的两个选项。这两种方法的优点/缺点是什么?是否有任何我没有考虑到的明显缺陷会使一个明显更好的选择?
考虑像\SomeFramework\Utilities\AwesomeClass
这样的示例类:
方法 1: 将每个 TestCase 类放入与被覆盖类相同的命名空间中。
\SomeFramework\Utilities\AwesomeClassTest
优势
与编写 PHPUnit 测试的传统方法一致。
缺点
灵活性较低。
似乎打破了使用命名空间背后的原则——不相关的测试被分组到同一个命名空间中。
方法 2: 将每个 TestCase 放在以所覆盖类命名的命名空间中。
\SomeFramework\Utilities\AwesomeClass\Test
优势
提供一种非常简单/明显的方式将多个相关的 TestCase 类组合在一起,例如针对不同的测试套件。
缺点
可能会产生更深、更复杂的层次结构。
【问题讨论】:
这取决于你。然而,方法 1 是最常见的,因为在引入命名空间之前它已经采用了这种方式。那么,您缺少哪种“灵活性”?在什么情况下它太不灵活了?而关于“打破原则”:为什么?测试的关联方式与类本身相互关联的方式相同。 PHPUnit best practices to organize tests的可能重复 【参考方案1】:我使用了第三个选项,它非常适合作曲家自动加载:在层次结构的第一步之后插入 Test
命名空间。在你的情况下,命名空间是\SomeFramework\Tests\Utilities\
,你的类是\SomeFramework\Tests\Utilities\AwesomeClassTest
。
然后,您可以将测试与\SomeFramework\Test
目录中的其他类放在一起,或者将它们放在单独的目录中。 composer.json
的自动加载信息可能如下所示:
"autoload":
"psr-0":
"SomeFramework\\": "src/",
,
"autoload-dev":
"psr-0":
"SomeFramework\\Tests\\": "tests/"
第三种方法的优点是:
测试和生产代码分离 用于测试和生产类的类似文件夹层次结构 轻松自动加载【讨论】:
既然测试不会在生产环境中自动加载,那么为什么将它们与生产代码分开会很重要?谁从中受益?这如何使自动加载更容易? @sanmai 自动加载文件在生产中会因为不必要的文件而变大? 在调试生产代码时,您不想一直跳过测试类。测试就是测试,生产代码就是生产代码。将它们分开,这样生产文件夹结构就不会被垃圾邮件发送,并且生产在理论上也可以在没有任何测试的情况下工作。【参考方案2】:我提出的解决方案及其背后的原因:
文件夹布局:
.
├── src
│ ├── bar
│ │ └── BarAwesomeClass.php
│ └── foo
│ └── FooAwesomeClass.php
└── tests
├── helpers
│ └── ProjectBaseTestClassWithHelperMethods.php
├── integration
│ ├── BarModuleTest.php
│ └── FooModuleTest.php
└── unit
├── bar
│ └── BarAwesomeClassTest.php
└── foo
└── FooAwesomeClassTest.php
helpers/
文件夹包含不是测试但仅在测试上下文中使用的类。通常该文件夹包含一个 BaseTestClass 可能包含项目特定的辅助方法和几个易于重用的存根类,因此您不需要那么多模拟。
integration/
文件夹包含跨越更多类和测试系统“更大”部分的测试。您没有那么多,但没有与生产类的 1:1 映射。
unit/
文件夹以 1:1 的比例映射到 src/
。因此,对于每个生产类,都有一个类包含该类的所有 单元 测试。
命名空间
方法 1:将每个 TestCase 类放在与被覆盖类相同的命名空间中。
这种文件夹方法应该可以解决您使用 方法 1 的缺点之一。您仍然可以灵活地进行比纯 1:1 映射所能提供的更多测试,但一切都已安排妥当。
似乎打破了使用命名空间背后的原则——不相关的测试被分组到同一个命名空间中。
如果测试感觉“不相关”,可能生产代码有同样的问题?
确实,测试并不相互依赖,但它们可能会使用它们的“关闭”类作为模拟,或者在 DTO 或值对象的情况下使用真实的类。所以我会说有联系。
方法 2:将每个 TestCase 放置在以覆盖类命名的命名空间中。
有几个项目可以做到这一点,但通常它们的结构略有不同:
不是\SomeFramework\Utilities\AwesomeClass\Test
,而是\SomeFramework\Tests\Utilities\AwesomeClassTest
,它们仍然保持 1:1 映射,但添加了额外的测试命名空间。
额外的测试命名空间
我个人的看法是,我不喜欢有单独的测试命名空间,我会尝试找到一对支持和反对该选择的论据:
测试应该作为如何使用类的文档
当真正的类在另一个命名空间中时,测试显示如何在它自己的模块之外使用该类。
当真正的类在同一个命名空间中时,测试会显示如何在该模块中使用该类。
差异很小(通常是几个“使用”语句或完全限定的路径)
当我们在 PHP 5.5 中可以使用 $this->getMock(AwesomeClass::CLASS)
而不是 $this->getMock('\SomeFramework\Utilities\AwesomeClass')
时,每个模拟都需要一个 use 语句。
对我来说,模块内的使用对大多数类来说更有价值
污染“生产”命名空间
当您说new \SomeFramework\Utilities\A
时,自动完成可能会显示AwesomeClass
和AwesomeClassTest
,而有些人不希望这样。对于外部使用,或者在运送您的源代码时,这当然不是问题,因为测试没有运送,但可能需要考虑。
【讨论】:
+1:感谢分享这为自己的决定提供了一些指导。 在你的目录结构中,为了保持真实的类在单元测试的同一个命名空间中(位于unit
目录)并且能够使用位于helpers
的类,如何配置 Composer autoload
和 autoload-dev
?【参考方案3】:
我更喜欢第一种方法来保持一致性——使用 PHPUnit 实践和我们的其他项目。此外,我只为每个被测类创建一个测试用例。将每个都放在自己的命名空间中似乎有点过分了。正如 KingCrunch 所说,测试是相关的,因为它们测试的类是相关的。
测试用例经常需要支持文件,例如固定装置,但这些文件很容易组织到为该类命名的子目录/命名空间中,并且通常在多个测试用例之间共享。
第二种方法的一大缺点是每个测试用例的名称都是Test
,这会产生多种后果:
【讨论】:
以上是关于在命名空间中组织 PHPUnit 测试 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
Phpunit 在测试类中仅模拟一种方法 - 使用 Mockery
在链配置的命名空间 App\Entity [Symfony 5.3][PHPUnit 8.5] 中找不到类 Mock_*