PHPUnit最佳实践来组织测试


74

我目前将从项目的phpunit测试开始。因此,我正在研究某些项目(例如Zend),以了解它们的工作方式以及如何组织测试。

大多数事情都很清楚,我唯一有问题的是如何正确组织测试套件。Zend有一个AllTests.php,可从中加载其他测试套件。
仔细查看该类是PHPUnit_Framework_TestSuite用来创建一个套件对象,然后向其中添加其他套件的,但是如果我查看PHPUnit文档以组织3.4之后的PHPUnit版本中的测试,则只有XML或FileHierarchy的描述。使用类来组织测试的一个已删除。
我尚未发现此方法已过时的任何东西,而Zend之类的项目仍在使用它。

但是,如果过时了,我将如何使用xml配置以相同的结构组织测试?执行所有测试都没问题,但是如果我只想执行几个测试,我将如何组织这些测试(在xml中)。也许在我仅指定要运行的几个测试/测试套件的地方创建几个xml?

因此,如果我只想测试应用程序的module1和module2,我会为每个应用程序增加一个xml,并只为其中的那些模块(模块使用的类)定义测试套件。还有为所有测试定义测试套件的工具吗?

还是@group在特定测试中使用注释将它们标记为module1或module2更好?

在此先感谢您为我提供了一些最佳做法。

Answers:


113

我将从链接到手册开始,然后进入我在该领域所见所闻。

组织phpunit测试套件

文件系统中的模块/测试文件夹组织

我推荐的方法是将文件系统与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执行时执行所有FactoryTestsphpunit tests/unit/logger/执行与日志记录相关的所有内容。

您可以将@group标签用于发行号,故事或其他内容,但对于“模块”,我将使用文件夹布局。

多个xml文件

如果需要,创建多个xml文件可能很有用:

  • 一个没有代码覆盖的
  • 一个仅用于单元测试(但不用于功能或集成或长期运行的测试)
  • 其他常见的“过滤器”情况
  • 以PHPBB3为例 their phpunit.xmls

测试的代码覆盖率

由于它与通过测试启动新项目有关:

  • 我的建议是使用博客中所述的@covers标签(仅用于单元测试,始终覆盖所有非公共功能,始终使用Cover标签。
  • 不要为您的集成测试生成覆盖率。它给您一种错误的安全感。
  • 始终使用白名单来包括所有生产代码,这样数字就不会对您说谎!

自动加载和引导测试

您不需要任何类型的自动加载即可进行测试。PHPUnit会解决这个问题。

使用该<phpunit bootstrap="file">属性可以指定测试引导程序。tests/bootstrap.php是放置它的好地方。在这里,您可以设置应用程序自动加载器等(或为此而调用应用程序引导程序)。

概要

  • 使用xml配置进行几乎所有操作
  • 单独的单元测试和集成测试
  • 您的单元测试文件夹应反映您的应用程序文件夹结构
  • 要仅执行特定测试,请使用phpunit --filterphpunit tests/unit/module1
  • strict从一开始就使用该模式,切勿将其关闭。

示例项目看


@TheCandyMan我的荣幸;随意加入SO或freenode #phpunit上的php聊天,如果您遇到问题;)
edorian 2011年

1
更新的博客文章链接:web.archive.org/web/20130402142841/http
cmt

@cmt感谢您的更新。我已经将帖子中的链接更新到我的新博客位置:edorian.github.io/…-干杯!
edorian

@edorian我应该在哪里放置phpunit.xml文件,我应该在哪里调用xml文件。
Kumar Shanmugam

4

基本目录结构

我一直在尝试将测试代码保持在要测试的代码旁边,实际上是在同一个目录中,文件名与带有正在测试的代码的文件略有不同。到目前为止,我喜欢这种方法。这样的想法是,您不必花费时间和精力来使目录结构在您的代码和测试代码之间保持同步。因此,如果您更改代码所在目录的名称,则无需查找并更改测试代码的目录名称。这也使您花费更少的时间查找与某些代码一起的测试代码,因为它就在旁边。这样一来,创建带有测试代码的文件就不再那么麻烦了,因为您不必首先查找带有测试的目录,可能会创建一个新目录以匹配您要为其创建测试的目录,然后创建测试文件。您只需在此处创建测试文件。

这样的一个巨大优势是,这意味着其他员工(不是您,因为您永远不会这样做)将不太可能避免编写测试代码,因为这太繁琐了。即使将方法添加到现有类中,由于查找测试代码的摩擦较小,它们也不太可能不会喜欢将测试添加到现有测试代码中。

一个缺点是,这使得在没有附带测试的情况下更难以发布生产代码。尽管如果使用严格的命名约定,则仍然有可能。例如,我一直在使用ClassName.php,ClassNameUnitTest.php和ClassNameIntegrationTest.php。当我想运行所有的单元测试时,有一个套件可以查找以UnitTest.php结尾的文件。集成测试套件的工作原理与此类似。如果需要,我可以使用类似的技术来防止测试发布到生产环境。

这种方法的另一个缺点是,当您只查找实际代码而不是测试代码时,需要花费更多的精力来区分两者。但是我觉得这实际上是一件好事,因为它迫使我们感到现实的痛苦,即测试代码也是代码,它增加了自己的维护成本,并且与其他任何东西一样,都是至关重要的一部分,而不是其他任何东西。只是东西偏向某处。

每班一个测试班:

对于大多数程序员来说,这还远不是实验性的,但是对我来说是这样。我正在尝试每个测试类只有一个测试类。过去,每个要测试的类都有一个完整的目录,然后在该目录中有几个类。每个测试类都以某种方式设置要测试的类,然后有很多方法,每个方法都有不同的断言。但是后来我开始注意到某些条件,我会将这些对象放入与从其他测试类进入的其他条件相同的东西。重复变得难以处理,因此我开始创建抽象来删除它。测试代码变得非常难以理解和维护。我意识到了这一点,但是我看不到对我有意义的替代方法。在每个类中只有一个测试类似乎似乎无法测试几乎足够的情况,而又无法将所有这些测试代码都包含在一个测试类中。现在,我对此有不同的看法。即使我是对的,对于其他想要编写和维护测试的程序员和我本人来说,这也是一个巨大的缓冲。现在,我正在尝试强迫自己在每个要测试的类中拥有一个测试类。如果我在一个测试类中遇到太多无法测试的事情,我正在尝试将其视为一种迹象,表明所测试的类做得太多,应该分成多个类。为了消除重复,我尝试尽可能地坚持更简单的抽象,以使所有内容都存在于一个可读的测试类中。

更新 我仍在使用和喜欢这种方法,但是我发现了一种很好的技术,可以减少测试代码和重复代码的数量。在测试类本身内部编写可重用的断言方法非常重要,该类中的测试方法经常使用这些方法。如果我认为它们是内部DSL,它可以帮助我提出正确类型的断言方法(Bob叔叔提倡的东西,实际上他实际上是在提倡内部DSL提倡)。有时,您可以通过接受具有简单值的字符串参数来进一步扩展此DSL概念(实际上是DSL),该值表示您要执行的测试类型。例如,有一次我制作了一个可重用的声明方法,该方法接受$ left,$ comparesAs和$ right参数。$this->assertCmp('a', '<', 'b')

老实说,我不能太强调这一点,这是使编写测试具有可持续性(您和其他程序员希望继续做)的全部基础。这使测试所增加的价值超过了他们所获得的价值。关键不是您需要使用那种确切的技术,而是要使用某种可重用的抽象,这些抽象允许您编写简短易读的测试。似乎我正在从这个问题中脱颖而出,但实际上并非如此。如果不这样做,最终将陷入陷入陷阱的陷阱,即需要为每个要测试的类创建多个测试类,然后事情就会真正崩溃。


到目前为止,这对我来说很好。我已经能够保持测试类和被测试类足够简单,以至于每个测试类只需要一个测试类。将测试课程与其他课程保持一致也很有效。现在,我正在尝试仅将这些测试类称为“ * Test.php”而不是“ * UnitTest.php”和“ * IntegrationTest.php”。我没有单元测试和集成测试,而是有测试。当这有助于避免模拟时,它们更类似于集成测试。即使测试实际上与数据库一起运行,它们也始终保持非常快的运行
still_dreaming_1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.