在命名空间中组织 PHPUnit 测试
我看到了将 PHPUnit 单元测试组织到命名空间层次结构中的两个选项.这两种方法的优点/缺点是什么?是否有任何我没有考虑到的明显缺陷会使一个明显更好的选择?
I see two options for organizing PHPUnit unit tests into a namespace hierarchy. What are the advantages/disadvantages to these two approaches? Are there any obvious flaws I haven't considered that would make one the obvious better choice?
考虑像 SomeFrameworkUtilitiesAwesomeClass
这样的示例类:
Consider a sample class like SomeFrameworkUtilitiesAwesomeClass
:
方法 1:将每个 TestCase 类放入与被覆盖类相同的命名空间中.
Approach 1: Place each TestCase class into the same namespace as the covered class.
SomeFrameworkUtilitiesAwesomeClassTest
- 优势
- 与编写 PHPUnit 测试的传统方法一致.
- 灵活性较低.
- 似乎打破了使用命名空间背后的原则——不相关的测试被分组到同一个命名空间中.
方法 2: 将每个 TestCase 放置在以覆盖类命名的命名空间中.
Approach 2: Place each TestCase in a namespace named after the covered class.
SomeFrameworkUtilitiesAwesomeClassTest
- 优势
- 提供一种非常简单/明显的方式将多个相关的 TestCase 类组合在一起,例如针对不同的测试套件.
- 可能会导致更深、更复杂的层次结构.
推荐答案
我提出的解决方案及其背后的原因:
My proposed solution and the reasoning behind it:
. ├── 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 可能包含项目特定的辅助方法和几个易于重用的存根类,因此您不需要那么多模拟.The
helpers/
folder contains classes that are not tests but are only used in a testing context. Usually that folder contains a BaseTestClass maybe containing project specific helper methods and a couple of easy to reuse stub classes so you don't need as many mocks.integration/
文件夹包含跨越更多类和测试更大"的测试.系统的组成部分.您没有那么多,但没有与生产类的 1:1 映射.The
integration/
folder contains tests that span over more classes and test "bigger" parts of the system. You don't have as many of them but there is no 1:1 mapping to production classes.unit/
文件夹 1:1 映射到src/
.因此,对于每个生产类,都有一个类包含该类的所有 单元 测试.The
unit/
folder maps 1:1 to thesrc/
. So for every production class there is one class that contains all the unit tests for that class.方法 1:将每个 TestCase 类放在与被覆盖类相同的命名空间中.
Approach 1: Place each TestCase class into the same namespace as the covered class.
这种文件夹方法应该可以解决您使用 方法 1 的缺点之一.您仍然可以灵活地进行比纯 1:1 映射所能提供的更多测试,但一切都已安排妥当.
This folder approach should solve one of your disadvantages with Approach 1. You still get the flexibility to have more tests than a pure 1:1 mapping could give you but everything is ordered and in place.
似乎打破了使用命名空间背后的原则——不相关的测试被分组到同一个命名空间中.
Seems to break the principle behind using namespaces - unrelated tests are grouped into the same namespace.
如果测试感觉不相关";也许生产代码有同样的问题?
If the tests feel "unrelated" maybe the production code has the same issue?
确实,这些测试并不相互依赖,但它们可能会使用它们的close".类作为模拟或在 DTO 或值对象的情况下使用真实的.所以我会说有联系.
It's true that the tests don't depend on one another but they might use their "close" classes as mocks or use the real ones in case of DTOs or Value Objects. So i'd say that there is a connection.
方法 2:将每个 TestCase 放置在以覆盖类命名的命名空间中.
Approach 2: Place each TestCase in a namespace named after the covered class.
有几个项目可以做到这一点,但通常它们的结构略有不同:
There are a couple of projects that do that but usually they structure it a little differently:
不是
SomeFrameworkUtilitiesAwesomeClassTest
,而是SomeFrameworkTestsUtilitiesAwesomeClassTest
并且它们仍然保持 1:1 映射,但使用添加了额外的测试命名空间.It's not
SomeFrameworkUtilitiesAwesomeClassTest
, butSomeFrameworkTestsUtilitiesAwesomeClassTest
and they still keep the 1:1 mapping, but with the extra test namespace added.我个人的看法是,我不喜欢有单独的测试命名空间,我会尝试找到一对支持和反对该选择的论据:
My personal take is that I don't like having separate test namespaces and I'll try to find a couple for arguments for and against that choice:
当真正的类在另一个命名空间中时,测试显示如何在它自己的模块之外使用该类.
When the real class is in another namespace, the tests show how to use that class outside of its own module.
当真正的类在同一个命名空间中时,测试会显示如何在该模块中使用该类.
When the real class is in the same namespace, the tests show how to use that class from inside that module.
差异很小(通常是几个使用"语句或完全限定的路径)
The differences are quite minor (usually a couple of "use" statements or fully-qualified paths)
当我们有可能在 PHP 5.5 中使用
$this->getMock(AwesomeClass::CLASS)
而不是$this->getMock('SomeFrameworkUtilitiesAwesomeClass')
每个模拟都需要一个 use 语句.When we get the possibility to say
$this->getMock(AwesomeClass::CLASS)
in PHP 5.5 instead of$this->getMock('SomeFrameworkUtilitiesAwesomeClass')
every mock will require a use statement.对我来说,模块内的使用对大多数类来说更有价值
For me the usage within the module is more valuable for most classes
当您说
new SomeFrameworkUtilitiesA
时,自动完成可能会显示AwesomeClass
和AwesomeClassTest
并且有些人不希望那.对于外部使用,或者在运送您的源代码时,这当然不是问题,因为测试没有运送,但可能需要考虑.When you say
new SomeFrameworkUtilitiesA
the auto completion might show youAwesomeClass
andAwesomeClassTest
and some people don't want that. For external use, or when shipping your source that isn't a problem of course since the tests don't get shipped but it might be something to consider.
相关文章