组织测试的PHPUnit最佳实践

时间:2022-02-23 07:06:56

I am currently going to start from scratch with the phpunit tests for a project. So I was looking into some projects (like Zend) to see how they are doing things and how they organizing their tests.

我现在将从一个项目的phpunit测试开始。所以我研究了一些项目(比如Zend),看看他们是如何做事情的,以及他们是如何组织测试的。

Most things are pretty clear, only thing I have some problems with is how to organize the test suites properly. Zend has an AllTests.php from which loads others test suites.
Tough looking at the class it is useing PHPUnit_Framework_TestSuite to create a suite object and then add the other suites to it, but if I look in the PHPUnit docs for organizing tests in PHPUnit versions after 3.4 there is only a description for XML or FileHierarchy. The one using classes to organize the tests was removed.
I haven't found anything that this method is deprecated and projects like Zend are still using it.

大多数事情都很清楚,我唯一的问题是如何正确地组织测试套件。Zend AllTests。用于加载其他测试套件的php。很难看到它使用PHPUnit_Framework_TestSuite来创建一个suite对象,然后向其添加其他套件,但是如果我在PHPUnit文档中查找在3.4之后以PHPUnit版本组织测试的PHPUnit文档,那么只有XML或FileHierarchy的描述。使用类组织测试的那个被删除了。我还没有发现任何不赞成这个方法的地方,像Zend这样的项目仍然在使用它。

But if it is deprecated, how would I be able to organize tests in the same structure with the xml configuration? Executing all tests is no problem, but how would I organize the tests (in the xml) if I only wanted to execute a few tests. Maybe creating several xmls where I only specify a few tests/test suites to be run?

但是,如果不赞成这样做,我如何能够用xml配置在相同的结构中组织测试呢?执行所有测试不是问题,但是如果我只想执行几个测试,那么我将如何组织测试(在xml中)。可能创建几个xml,而我只指定要运行的几个测试/测试套件?

So if I would want to only test module1 and module2 of the application, would I have an extra xml for each and defining test suites only for those modules (classes used by the module) in it. And also one that defines a test suite for all tests?

因此,如果我只想测试应用程序的module1和module2,那么我是否会为每个模块提供额外的xml并只为其中的模块(模块使用的类)定义测试套件。还有一个定义了所有测试的测试套件?

Or would it be better to use the @group annotation on the specific tests to mark them to be for module1 or module2?

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

Thanks in advance for pointing me to some best practices.

提前谢谢你给我指出了一些最佳实践。

2 个解决方案

#1


96  

I'll start of by linking to the manual and then going into what I've seen and heard in the field.

我将从链接到手册开始,然后进入我在这个领域中看到和听到的内容。

Organizing phpunit test suites

组织phpunit)测试套件

Module / Test folder organization in the file system

My recommended approach is combining the file system with an xml config.

我推荐的方法是将文件系统与xml配置相结合。

tests/
 \ unit/
 | - module1
 | - module2
 - integration/
 - functional/

with a phpunit.xml with a simple:

phpunit)。xml用一个简单的:

<testsuites>
  <testsuite name="My whole project">
    <directory>tests</directory>
  </testsuite>
</testsuites>

you can split the testsuites if you want to but thats a project to project choice.

如果愿意,可以将testsuite分开,但这是一个项目选择。

Running phpunit will then execute ALL tests and running phpunit tests/unit/module1 will run all tests of module1.

运行phpunit将执行所有测试,运行phpunit测试/单元/module1将运行module1的所有测试。

Organization of the "unit" folder

The most common approach here is to mirror your source/ directory structure in your tests/unit/ folder structure.

这里最常见的方法是在测试/单元/文件夹结构中镜像源/目录结构。

You have one TestClass per ProductionClass anyways so it's a good approach in my book.

每个ProductionClass都有一个TestClass,在我的书中这是一个很好的方法。

In file organization

  • One class per file.
  • 每个文件一个类。

It's not going to work anyways if you have more than one test class in one file so avoid that pitfall.

如果在一个文件中有多个测试类,那么它也不会起作用,所以要避免这个陷阱。

  • Don't have a test namespace
  • 不要使用测试名称空间

It just makes writing the test more verbose as you need an additional use statement so I'd say the testClass should go in the same namespace as the production class but that is nothing PHPUnit forces you to do. I've just found it to be easier with no drawbacks.

它只会使编写测试变得更加冗长,因为您需要一个额外的use语句,所以我认为testClass应该与生产类位于同一个名称空间中,但是PHPUnit并没有强迫您这么做。我发现它更简单,没有缺点。

Executing only a few tests

For example phpunit --filter Factory executes all FactoryTests while phpunit tests/unit/logger/ executes everything logging related.

例如,phpunit——filter Factory执行所有出厂测试,而phpunit test /unit/logger/执行与日志记录相关的所有东西。

You can use @group tags for something like issue numbers, stories or something but for "modules" I'd use the folder layout.

你可以使用@group标签来标记一些东西,比如问题号,故事或者其他东西,但是对于“模块”,我将使用文件夹布局。

Multiple xml files

It can be useful to create multiple xml files if you want to have:

如果您想要有:

  • one without code coverage
  • 没有代码覆盖率
  • one just for the unit tests (but not for the functional or integration or long running tests)
  • 一个只用于单元测试(但不用于功能或集成或长期运行测试)
  • other common "filter" cases
  • 其他常见的“过滤器”案件
  • PHPBB3 for example does that for their phpunit.xmls
  • 例如,PHPBB3就为PHPBB3提供了这样的功能。

Code coverage for your tests

As it is related to starting a new project with tests:

因为它与启动一个新项目有关:

  • My suggestion is to use @covers tags like described in my blog (Only for unit tests, always cover all non public functions, always use covers tags.
  • 我的建议是使用@cover标签,就像我博客中描述的那样(仅用于单元测试,总是覆盖所有非公共功能,总是使用cover标签)。
  • Don't generate coverage for your integration tests. It gives you a false sense of security.
  • 不要为集成测试生成覆盖。它给你一种虚假的安全感。
  • Always use whitelisting to include all of your production code so the numbers don't lie to you!
  • 总是使用白名单包括你所有的生产代码,所以数字不会欺骗你!

Autoloading and bootstrapping your tests

You don't need any sort of auto loading for your tests. PHPUnit will take care of that.

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

Use the <phpunit bootstrap="file"> attribute to specify your test bootstrap. tests/bootstrap.php is a nice place to put it. There you can set up your applications autoloader and so on (or call your applications bootstrap for that matter).

使用 属性指定测试引导。测试/引导。php是放置它的好地方。在那里,您可以设置应用程序自动加载器等(或调用您的应用程序启动程序)。

Summary

  • Use the xml configuration for pretty much everything
  • 几乎所有的东西都使用xml配置
  • Seperate unit and integration tests
  • 分离单元和集成测试。
  • Your unit test folders should mirror your applications folder structure
  • 单元测试文件夹应该反映应用程序文件夹结构
  • To only execute specif tests use phpunit --filter or phpunit tests/unit/module1
  • 为了只执行特殊测试,使用phpunit -filter或phpunit test /unit/module1
  • Use the strict mode from the get go and never turn it off.
  • 使用严格的模式,不要关掉它。

Sample projects to look at

#2


0  

Basic Directory Structure:

基本目录结构:

I have been experimenting with keeping the test code right next to the code being tested, literally in the same directory with a slightly different file name from the file with the code it is testing. So far I am liking this approach. The idea is you don't have to spend time and energy keeping the directory structure in sync between your code and your test code. So if you change the name of the directory the code is in, you don't then also need to go and find and change the directory name for the test code. This also causes you to spend less time looking for the test code that goes with some code as it is right there next to it. This even makes it less of a hassle to create the file with the test code to begin with because you don't have to first find the directory with the tests, possibly create a new directory to match the one you are creating tests for, and then create the test file. You just create the test file right there.

我一直在试验将测试代码保留在正在测试的代码旁边,实际上是在同一个目录中,与正在测试的代码的文件名稍有不同。到目前为止,我喜欢这种方法。其思想是您不必花费时间和精力在代码和测试代码之间保持目录结构的同步。因此,如果您更改了代码所在目录的名称,那么您就不需要去查找并更改测试代码的目录名。这也使您花更少的时间寻找与某些代码相关的测试代码,因为它就在它旁边。这甚至更少的麻烦与测试代码创建的文件开始,因为你不需要首先找到与测试目录,可能创建一个新目录匹配创建测试,然后创建测试文件。您只需在那里创建测试文件。

One huge advantage of this is it means the other employees (not you because you would never do this) will be less likely to avoid writing test code to begin with because it is just too much work. Even as they add methods to existing classes they will be less likely to not feel like adding tests to the existing test code.

这样做的一个巨大优势是,它意味着其他员工(不是您,因为您永远不会这样做)将不太可能避免编写测试代码,因为这是太多的工作。即使他们将方法添加到现有的类中,他们也不太可能不愿意将测试添加到现有的测试代码中。

One disadvantage is this makes it harder to release your production code without the tests accompanying it. Although if you use strict naming conventions it still might be possible. For example, I have been using ClassName.php, ClassNameUnitTest.php, and ClassNameIntegrationTest.php. When I want to run all the unit tests, there is a suite that looks for files ending in UnitTest.php. The integration test suite works similarly. If I wanted to, I could use a similar technique to prevent the tests from getting released to production.

一个缺点是,如果没有附带的测试,就很难发布产品代码。尽管如果使用严格的命名约定,这仍然是可能的。例如,我一直在使用ClassName。php,ClassNameUnitTest。php和ClassNameIntegrationTest.php。当我想运行所有单元测试时,有一个套件可以查找以UnitTest.php结尾的文件。集成测试套件的工作方式类似。如果我愿意,我可以使用类似的技术来阻止测试被发布到产品中。

Another disadvantage of this approach is when you are just looking for actual code, not test code, it takes a little more effort to differentiate between the two.

这种方法的另一个缺点是,当您只是寻找实际的代码而不是测试代码时,您需要花费更多的精力来区分这两个代码。

One test class per class:

每个班一个测试班:

This is far from experimental for most programmers, but it is for me. I am experimenting with only having one test class per class being tested. In the past I had an entire directory for each class being tested and then I had several classes inside that directory. Each test class setup the class being tested in a certain way, and then had a bunch of methods each one with a different assertion made. But then I started noticing certain conditions I would get these objects into had stuff in common with other conditions it got into from other test classes. The duplication become too much to handle, so I started creating abstractions to remote it. The test code became very difficult to understand and maintain. I realized this, but I couldn't see an alternative that made sense to me. Just having one test class per class seemed like it would not be able to test nearly enough situations without becoming overwhelming to have all that test code inside one test class. Now I have a different perspective on it. Even if I was right, this is a huge dampener on other programmers, and myself, wanting to write and maintain the tests. Now I am experimenting with forcing myself to have one test class per class being tested. If I run into too many things to test in that one test class, I am experimenting with seeing this as an indication that the class being tested is doing too much, and should be broken up into multiple classes. For removing duplication I am trying to stick to simpler abstractions as much as possible that allows everything to exist in one readable test class.

对于大多数程序员来说,这还不是实验性的,但对我来说却是如此。我正在试验每堂课只有一个测试班。在过去,我对每个正在测试的类都有一个完整的目录,然后在该目录中有几个类。每个测试类都以某种方式对类进行了测试,然后有一堆方法,每个方法都有不同的断言。但后来我开始注意到某些条件我会把这些东西和其他测试类的其他条件结合起来。复制变得难以处理,因此我开始创建抽象来远程处理它。测试代码变得非常难以理解和维护。我意识到这一点,但我找不到对我有意义的替代方案。每个类都有一个测试类似乎无法测试足够多的情况,而不需要在一个测试类中拥有所有的测试代码。现在我有了不同的看法。即使我是对的,这对其他程序员和我来说也是一个巨大的打击,因为我想编写和维护测试。现在,我正在尝试强迫自己在每堂课上都有一个测试课。如果我在一个测试类中遇到了太多要测试的东西,那么我将把它看作是一个信号,表明正在测试的类做了太多的工作,应该被分成多个类。为了消除重复,我尽量使用更简单的抽象,使所有东西都存在于一个可读的测试类中。

#1


96  

I'll start of by linking to the manual and then going into what I've seen and heard in the field.

我将从链接到手册开始,然后进入我在这个领域中看到和听到的内容。

Organizing phpunit test suites

组织phpunit)测试套件

Module / Test folder organization in the file system

My recommended approach is combining the file system with an xml config.

我推荐的方法是将文件系统与xml配置相结合。

tests/
 \ unit/
 | - module1
 | - module2
 - integration/
 - functional/

with a phpunit.xml with a simple:

phpunit)。xml用一个简单的:

<testsuites>
  <testsuite name="My whole project">
    <directory>tests</directory>
  </testsuite>
</testsuites>

you can split the testsuites if you want to but thats a project to project choice.

如果愿意,可以将testsuite分开,但这是一个项目选择。

Running phpunit will then execute ALL tests and running phpunit tests/unit/module1 will run all tests of module1.

运行phpunit将执行所有测试,运行phpunit测试/单元/module1将运行module1的所有测试。

Organization of the "unit" folder

The most common approach here is to mirror your source/ directory structure in your tests/unit/ folder structure.

这里最常见的方法是在测试/单元/文件夹结构中镜像源/目录结构。

You have one TestClass per ProductionClass anyways so it's a good approach in my book.

每个ProductionClass都有一个TestClass,在我的书中这是一个很好的方法。

In file organization

  • One class per file.
  • 每个文件一个类。

It's not going to work anyways if you have more than one test class in one file so avoid that pitfall.

如果在一个文件中有多个测试类,那么它也不会起作用,所以要避免这个陷阱。

  • Don't have a test namespace
  • 不要使用测试名称空间

It just makes writing the test more verbose as you need an additional use statement so I'd say the testClass should go in the same namespace as the production class but that is nothing PHPUnit forces you to do. I've just found it to be easier with no drawbacks.

它只会使编写测试变得更加冗长,因为您需要一个额外的use语句,所以我认为testClass应该与生产类位于同一个名称空间中,但是PHPUnit并没有强迫您这么做。我发现它更简单,没有缺点。

Executing only a few tests

For example phpunit --filter Factory executes all FactoryTests while phpunit tests/unit/logger/ executes everything logging related.

例如,phpunit——filter Factory执行所有出厂测试,而phpunit test /unit/logger/执行与日志记录相关的所有东西。

You can use @group tags for something like issue numbers, stories or something but for "modules" I'd use the folder layout.

你可以使用@group标签来标记一些东西,比如问题号,故事或者其他东西,但是对于“模块”,我将使用文件夹布局。

Multiple xml files

It can be useful to create multiple xml files if you want to have:

如果您想要有:

  • one without code coverage
  • 没有代码覆盖率
  • one just for the unit tests (but not for the functional or integration or long running tests)
  • 一个只用于单元测试(但不用于功能或集成或长期运行测试)
  • other common "filter" cases
  • 其他常见的“过滤器”案件
  • PHPBB3 for example does that for their phpunit.xmls
  • 例如,PHPBB3就为PHPBB3提供了这样的功能。

Code coverage for your tests

As it is related to starting a new project with tests:

因为它与启动一个新项目有关:

  • My suggestion is to use @covers tags like described in my blog (Only for unit tests, always cover all non public functions, always use covers tags.
  • 我的建议是使用@cover标签,就像我博客中描述的那样(仅用于单元测试,总是覆盖所有非公共功能,总是使用cover标签)。
  • Don't generate coverage for your integration tests. It gives you a false sense of security.
  • 不要为集成测试生成覆盖。它给你一种虚假的安全感。
  • Always use whitelisting to include all of your production code so the numbers don't lie to you!
  • 总是使用白名单包括你所有的生产代码,所以数字不会欺骗你!

Autoloading and bootstrapping your tests

You don't need any sort of auto loading for your tests. PHPUnit will take care of that.

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

Use the <phpunit bootstrap="file"> attribute to specify your test bootstrap. tests/bootstrap.php is a nice place to put it. There you can set up your applications autoloader and so on (or call your applications bootstrap for that matter).

使用 属性指定测试引导。测试/引导。php是放置它的好地方。在那里,您可以设置应用程序自动加载器等(或调用您的应用程序启动程序)。

Summary

  • Use the xml configuration for pretty much everything
  • 几乎所有的东西都使用xml配置
  • Seperate unit and integration tests
  • 分离单元和集成测试。
  • Your unit test folders should mirror your applications folder structure
  • 单元测试文件夹应该反映应用程序文件夹结构
  • To only execute specif tests use phpunit --filter or phpunit tests/unit/module1
  • 为了只执行特殊测试,使用phpunit -filter或phpunit test /unit/module1
  • Use the strict mode from the get go and never turn it off.
  • 使用严格的模式,不要关掉它。

Sample projects to look at

#2


0  

Basic Directory Structure:

基本目录结构:

I have been experimenting with keeping the test code right next to the code being tested, literally in the same directory with a slightly different file name from the file with the code it is testing. So far I am liking this approach. The idea is you don't have to spend time and energy keeping the directory structure in sync between your code and your test code. So if you change the name of the directory the code is in, you don't then also need to go and find and change the directory name for the test code. This also causes you to spend less time looking for the test code that goes with some code as it is right there next to it. This even makes it less of a hassle to create the file with the test code to begin with because you don't have to first find the directory with the tests, possibly create a new directory to match the one you are creating tests for, and then create the test file. You just create the test file right there.

我一直在试验将测试代码保留在正在测试的代码旁边,实际上是在同一个目录中,与正在测试的代码的文件名稍有不同。到目前为止,我喜欢这种方法。其思想是您不必花费时间和精力在代码和测试代码之间保持目录结构的同步。因此,如果您更改了代码所在目录的名称,那么您就不需要去查找并更改测试代码的目录名。这也使您花更少的时间寻找与某些代码相关的测试代码,因为它就在它旁边。这甚至更少的麻烦与测试代码创建的文件开始,因为你不需要首先找到与测试目录,可能创建一个新目录匹配创建测试,然后创建测试文件。您只需在那里创建测试文件。

One huge advantage of this is it means the other employees (not you because you would never do this) will be less likely to avoid writing test code to begin with because it is just too much work. Even as they add methods to existing classes they will be less likely to not feel like adding tests to the existing test code.

这样做的一个巨大优势是,它意味着其他员工(不是您,因为您永远不会这样做)将不太可能避免编写测试代码,因为这是太多的工作。即使他们将方法添加到现有的类中,他们也不太可能不愿意将测试添加到现有的测试代码中。

One disadvantage is this makes it harder to release your production code without the tests accompanying it. Although if you use strict naming conventions it still might be possible. For example, I have been using ClassName.php, ClassNameUnitTest.php, and ClassNameIntegrationTest.php. When I want to run all the unit tests, there is a suite that looks for files ending in UnitTest.php. The integration test suite works similarly. If I wanted to, I could use a similar technique to prevent the tests from getting released to production.

一个缺点是,如果没有附带的测试,就很难发布产品代码。尽管如果使用严格的命名约定,这仍然是可能的。例如,我一直在使用ClassName。php,ClassNameUnitTest。php和ClassNameIntegrationTest.php。当我想运行所有单元测试时,有一个套件可以查找以UnitTest.php结尾的文件。集成测试套件的工作方式类似。如果我愿意,我可以使用类似的技术来阻止测试被发布到产品中。

Another disadvantage of this approach is when you are just looking for actual code, not test code, it takes a little more effort to differentiate between the two.

这种方法的另一个缺点是,当您只是寻找实际的代码而不是测试代码时,您需要花费更多的精力来区分这两个代码。

One test class per class:

每个班一个测试班:

This is far from experimental for most programmers, but it is for me. I am experimenting with only having one test class per class being tested. In the past I had an entire directory for each class being tested and then I had several classes inside that directory. Each test class setup the class being tested in a certain way, and then had a bunch of methods each one with a different assertion made. But then I started noticing certain conditions I would get these objects into had stuff in common with other conditions it got into from other test classes. The duplication become too much to handle, so I started creating abstractions to remote it. The test code became very difficult to understand and maintain. I realized this, but I couldn't see an alternative that made sense to me. Just having one test class per class seemed like it would not be able to test nearly enough situations without becoming overwhelming to have all that test code inside one test class. Now I have a different perspective on it. Even if I was right, this is a huge dampener on other programmers, and myself, wanting to write and maintain the tests. Now I am experimenting with forcing myself to have one test class per class being tested. If I run into too many things to test in that one test class, I am experimenting with seeing this as an indication that the class being tested is doing too much, and should be broken up into multiple classes. For removing duplication I am trying to stick to simpler abstractions as much as possible that allows everything to exist in one readable test class.

对于大多数程序员来说,这还不是实验性的,但对我来说却是如此。我正在试验每堂课只有一个测试班。在过去,我对每个正在测试的类都有一个完整的目录,然后在该目录中有几个类。每个测试类都以某种方式对类进行了测试,然后有一堆方法,每个方法都有不同的断言。但后来我开始注意到某些条件我会把这些东西和其他测试类的其他条件结合起来。复制变得难以处理,因此我开始创建抽象来远程处理它。测试代码变得非常难以理解和维护。我意识到这一点,但我找不到对我有意义的替代方案。每个类都有一个测试类似乎无法测试足够多的情况,而不需要在一个测试类中拥有所有的测试代码。现在我有了不同的看法。即使我是对的,这对其他程序员和我来说也是一个巨大的打击,因为我想编写和维护测试。现在,我正在尝试强迫自己在每堂课上都有一个测试课。如果我在一个测试类中遇到了太多要测试的东西,那么我将把它看作是一个信号,表明正在测试的类做了太多的工作,应该被分成多个类。为了消除重复,我尽量使用更简单的抽象,使所有东西都存在于一个可读的测试类中。