我看到了将PHPUnit单元测试组织到命名空间层次结构中的两个选项。这两种方法有哪些优点/缺点?是否有任何明显的缺陷我没有考虑过会使一个明显更好的选择?
考虑像\SomeFramework\Utilities\AwesomeClass
这样的示例类:
方法1:将每个TestCase类放入与覆盖类相同的命名空间。
\SomeFramework\Utilities\AwesomeClassTest
方法2:将每个TestCase放在以覆盖类命名的命名空间中。
\SomeFramework\Utilities\AwesomeClass\Test
答案 0 :(得分:31)
我提出的解决方案及其背后的推理:
.
├── 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')
时,每个mock都需要一个use语句。
对我来说,模块中的用法对大多数类来说更有价值
当您说new \SomeFramework\Utilities\A
时,自动完成功能可能会显示AwesomeClass
和AwesomeClassTest
,有些人不希望这样。对于外部使用,或者在运输您的源时当然不是问题,因为测试没有发货,但可能需要考虑。
答案 1 :(得分:13)
我使用的第三个选项与编辑器自动加载非常吻合:在层次结构的第一步之后插入Test
命名空间。在您的情况下,名称空间为\SomeFramework\Tests\Utilities\
,您的类将为\SomeFramework\Tests\Utilities\AwesomeClassTest
。
然后,您可以将测试与其他类一起放在\SomeFramework\Test
目录中,或者将它们放在单独的目录中。 composer.json
的自动加载信息可能如下所示:
{
"autoload": {
"psr-0": {
"SomeFramework\\": "src/",
}
},
"autoload-dev": {
"psr-0": {
"SomeFramework\\Tests\\": "tests/"
}
}
}
第三种方法的优点是:
答案 2 :(得分:4)
我更喜欢第一种保持一致性的方法 - 使用PHPUnit实践和我们的其他项目。此外,我每个测试类只创建一个测试用例。将每个放在自己的命名空间中似乎有点矫枉过正。正如KingCrunch所说,测试是相关的,因为他们测试的类是相关的。
测试用例经常需要支持文件(如fixture),但这些文件很容易组织到为类命名的子目录/命名空间中,并且通常在多个测试用例之间共享。
第二种方法的一个很大的缺点是每个测试用例的名称都是Test
,它会有几个分支: