我目前要从头开始使用phpunit测试项目。所以我正在研究一些项目(比如Zend),看看他们是如何做的以及他们如何组织他们的测试。
大多数事情都很清楚,只有我遇到的一些问题是如何正确组织测试套件。
Zend有一个AllTests.php,从中加载其他测试套件
艰难地看一下它正在使用PHPUnit_Framework_TestSuite
创建一个套件对象,然后将其他套件添加到它中,但如果我查看PHPUnit文档在3.4之后的PHPUnit版本中组织测试,那么只有XML的描述或FileHierarchy。使用类来组织测试的那个被删除了
我没有发现任何不推荐使用此方法的内容,以及像Zend这样的项目仍在使用它。
但如果不推荐使用,我怎么能用xml配置在相同的结构中组织测试?执行所有测试都没有问题,但如果我只想执行一些测试,我将如何组织测试(在xml中)。也许创建几个xmls,我只指定要运行的几个测试/测试套件?
因此,如果我只想测试应用程序的module1和module2,我是否每个都有一个额外的xml,并且只为那些模块(模块使用的类)定义测试套件。还有一个为所有测试定义测试套件的?
或者在特定测试中使用@group
注释将它们标记为module1或module2会更好吗?
提前感谢您指点一些最佳做法。
答案 0 :(得分:104)
我将首先链接到手册,然后进入我在现场看到和听到的内容。
Organizing phpunit test suites
我推荐的方法是将文件系统与xml配置相结合。
tests/
\ unit/
| - module1
| - module2
- integration/
- functional/
使用简单的phpunit.xml
:
<testsuites>
<testsuite name="My whole project">
<directory>tests</directory>
</testsuite>
</testsuites>
如果您愿意,可以拆分测试套件,但这是项目选择的项目。
运行phpunit
然后执行所有测试,运行phpunit tests/unit/module1
将运行module1的所有测试。
此处最常见的方法是在source/
文件夹结构中镜像您的tests/unit/
目录结构。
无论如何,每个ProductionClass都有一个TestClass,所以这是我书中的一个好方法。
如果你在一个文件中有多个测试类,那么它无论如何都不会起作用,所以要避免这个陷阱。
它只是让编写测试更加冗长,因为你需要一个额外的use语句,所以我说testClass应该和生产类一样在同一个命名空间中,但这不是PHPUnit强迫你做的。我刚刚发现它更容易没有任何缺点。
例如phpunit --filter Factory
执行所有FactoryTests,而phpunit tests/unit/logger/
执行与日志相关的所有内容。
您可以将@group
标签用于问题编号,故事或其他内容,但对于“模块”,我会使用文件夹布局。
如果您想要创建多个xml文件,这将非常有用:
their phpunit.xmls
因为它与使用测试开始一个新项目有关:
@covers
代码like described in my blog(仅适用于单元测试,始终涵盖所有非公开功能,请始终使用封面代码。您的测试不需要任何类型的自动加载。 PHPUnit将负责这一点。
使用<phpunit bootstrap="file">
属性指定测试引导程序。 tests/bootstrap.php
是一个不错的地方。在那里,您可以设置自动加载器等应用程序(或者就此问题调用应用程序引导程序)。
phpunit --filter
或phpunit tests/unit/module1
strict
模式,切勿将其关闭。 答案 1 :(得分:1)
基本目录结构:
我一直在尝试将测试代码保持在正在测试的代码旁边,字面上在同一目录中,文件名与文件名略有不同,并且正在测试代码。到目前为止,我喜欢这种方法。我们的想法是,您不必花时间和精力保持代码与测试代码之间的目录结构同步。因此,如果您更改代码所在目录的名称,那么您也不需要去查找和更改测试代码的目录名称。这也使您花费更少的时间来寻找与某些代码相关的测试代码,因为它就在旁边。这甚至可以减少使用测试代码创建文件的麻烦,因为您不必首先找到包含测试的目录,可能创建一个新目录以匹配您正在创建测试的目录,然后创建测试文件。您只需在那里创建测试文件。
这方面的一个巨大优势是,它意味着其他员工(不是因为你永远不会这样做)将不太可能避免编写测试代码,因为它只是太多的工作。即使他们在现有类中添加方法,他们也不太可能不想在现有测试代码中添加测试,因为找到测试代码的摩擦很小。
一个缺点是,如果没有附带的测试,就很难发布生产代码。虽然如果使用严格的命名约定,它仍然可能。例如,我一直在使用ClassName.php,ClassNameUnitTest.php和ClassNameIntegrationTest.php。当我想运行所有单元测试时,有一个套件可以查找以UnitTest.php结尾的文件。集成测试套件的工作方式类似。如果我愿意,我可以使用类似的技术来防止测试被释放到生产中。
这种方法的另一个缺点是,当您只是在寻找实际代码而不是测试代码时,需要花费更多精力来区分这两者。但我觉得这实际上是一件好事,因为它迫使我们感受到现实的痛苦,即测试代码也是代码,它增加了它的代码。拥有维护成本,并且与其他任何东西一样,只是代码的一部分,而不仅仅是在某个地方的某些东西。
每个班级一个测试类:
对于大多数程序员来说,这远非实验性的,但它适合我。我正在尝试每个类只测试一个测试类。在过去,我为每个正在测试的类都有一个完整的目录,然后我在该目录中有几个类。每个测试类以某种方式设置要测试的类,然后有一堆方法,每个方法都有不同的断言。但后来我开始注意到某些条件,我会将这些对象与其他测试类中的其他条件相同。复制变得太难以处理,所以我开始创建抽象来删除它。测试代码变得非常难以理解和维护。我意识到了这一点,但我无法看到对我有意义的替代方案。每个类只有一个测试类似乎无法测试几乎足够的情况而不会在一个测试类中包含所有测试代码。现在我对它有不同的看法。即使我是对的,这对其他程序员和我自己来说也是一个巨大的阻碍,他们想要编写和维护测试。现在我正在尝试强迫自己每个类测试一个测试类。如果我在一个测试类中遇到太多要测试的东西,我正在尝试将此视为一个指示,即被测试的类做得太多,应该分解为多个类。为了消除重复,我试图尽可能地遵循更简单的抽象,允许一切都存在于一个可读的测试类中。
<强>更新强>
我仍在使用和喜欢这种方法,但我发现了一种非常好的技术来减少测试代码的数量和重复的数量。在测试类本身中编写可重用的断言方法非常重要,这些方法会被该类中的测试方法大量使用。如果我将它们视为内部DSL(Bob叔叔推广的东西,实际上他实际上是在制作内部DSL),它可以帮助我提出正确类型的断言方法。有时你可以通过接受一个字符串参数来进一步采用这种DSL概念(实际上是DSL),该参数具有一个简单的值,指的是你试图执行什么样的测试。例如,有一次我做了一个可重用的dissertion方法,接受$ left,$ comparisonAs和$ right参数。这使得测试非常简短且可读,因为代码读起来像$this->assertCmp('a', '<', 'b')
。
老实说,我不能强调这一点,它是使写作测试成为可持续的整个基础(你和其他程序员想要继续做)。它使得测试所增加的价值超过他们带走的价值成为可能。关键不在于你需要使用那种确切的技术,关键是你需要使用某种可重用的抽象,它允许你编写简短易读的测试。看起来我似乎从问题中脱离了主题,但我真的不是。如果你不这样做,你最终会陷入需要为每个被测试的类创建多个测试类的陷阱,并且事情真的会从那里崩溃。