在集成测试中并行PHPUnit测试

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

As the time needed for run complete PHPUnit suite raises, our team starts wondering if there is a possibility to run Unit tests in parallel. Recently I read an article about Paraunit, also Sebastian Bergman wrote, he'll add parallelism into PHPUnit 3.7.

随着运行完成PHPUnit套件所需的时间增加,我们的团队开始怀疑是否有可能并行运行单元测试。最近我读了一篇关于Paraunit的文章,Sebastian Bergman写道,他将在PHPUnit 3.7中添加并行性。

But there remains the problem with integration tests, or, more generally, tests that interact with DB. For the sake of consistency, the testDB has to be resetted and fixtures loaded after each test. But in parallel tests there is a problem with race conditions, because all processes use the same DB.

但是,集成测试仍然存在问题,或者更常见的是与DB交互的测试。为了保持一致性,必须重置testDB并在每次测试后加载夹具。但在并行测试中,竞争条件存在问题,因为所有进程都使用相同的DB。

So to be able to run integration tests in parallel, we have to assign own database to each process. I would like to ask, if someone has some thoughts about how this problem can be solved. Maybe there are already implemented solutions to this problem in another xUnit implementation.

因此,为了能够并行运行集成测试,我们必须为每个进程分配自己的数据库。我想问一下,如果有人对如何解决这个问题有一些想法。也许在另一个xUnit实现中已经实现了这个问题的解决方案。

In my team we are using MongoDB, so one solution would be to programmatically create a config file for each PHPUnit process, with generated DB name(for this process), and in setUp() method we could clone the main TestDb into this temporary one. But before we start to implement this approach I would like to ask for your ideas about the topic.

在我的团队中,我们使用MongoDB,因此一个解决方案是以编程方式为每个PHPUnit进程创建一个配置文件,生成数据库名称(对于此进程),在setUp()方法中,我们可以将主要TestDb克隆到此临时数据库中。但在我们开始实施这种方法之前,我想询问您对该主题的看法。

3 个解决方案

#1


4  

This is a good question: preparing for parallel unit tests is going to require learning some new Best Practices, and I suspect some of them are going to slow our tests down.

这是一个很好的问题:准备并行单元测试需要学习一些新的最佳实践,我怀疑它们中的一些将会减慢我们的测试速度。

At the highest level, the advice is: avoid testing with a database wherever possible. Abstract all interactions with your database, and then mock that class. But you've already noted your question is about integration tests, where this is not possible.

在*别,建议是:尽可能避免使用数据库进行测试。摘要与数据库的所有交互,然后模拟该类。但是你已经注意到你的问题是关于集成测试,这是不可能的。

When using PDO, I generally use sqlite::memory: Each test gets its own database. It is anonymous and automatically cleaned up when the test ends. (But I noted some problems with this when you real application is not using sqlite: Suggestions to avoid DB deps when using an in-memory sqlite DB to speed up unit tests )

使用PDO时,我通常使用sqlite :: memory:每个测试都有自己的数据库。它是匿名的,并在测试结束时自动清理。 (但是当你真正的应用程序没有使用sqlite时,我注意到了一些问题:当使用内存中的sqlite DB来加速单元测试时,建议避免使用DB deps)

When using a database that does not have an in-memory choice, create the database with a random name. If the parallelization is at the PHPUnit process level, quite coarse, you could use the process pid. But that has no real advantages over a random name. (I know PHP is single-threaded, but perhaps in future we would have a custom phpUnit module, that uses threads to run tests in parallel; we might as well be ready for that.)

使用没有内存选择的数据库时,请使用随机名称创建数据库。如果并行化处于PHPUnit进程级别,非常粗略,则可以使用进程pid。但这比随机名称没有真正的优势。 (我知道PHP是单线程的,但是将来我们可能会有一个自定义的phpUnit模块,它使用线程并行运行测试;我们也可以为此做好准备。)

If you have the xUnit Test Patterns book, chapter 13 is about testing databases (relatively short). Chapters 8 and 9 on transient vs. persistent fixtures are useful too. And, of course, most of the book is on abstraction layers to make mocking easier :-)

如果你有xUnit测试模式书,第13章是关于测试数据库(相对较短)。关于瞬态与持续固定装置的第8章和第9章也很有用。而且,当然,本书的大部分内容都是关于抽象层,以使模拟变得更容易:-)

#2


0  

But there remains the problem with integration tests, or, more generally, tests that interact with DB. For the sake of consistency, the testDB has to be resetted and fixtures loaded after each test. But in parallel tests there is a problem with race conditions, because all processes use the same DB.

但是,集成测试仍然存在问题,或者更常见的是与DB交互的测试。为了保持一致性,必须重置testDB并在每次测试后加载夹具。但在并行测试中,竞争条件存在问题,因为所有进程都使用相同的DB。

So to be able to run integration tests in parallel, we have to assign own database to each process. I would like to ask, if someone has some thoughts about how this problem can be solved. Maybe there are already implemented solutions to this problem in another xUnit implementation.

因此,为了能够并行运行集成测试,我们必须为每个进程分配自己的数据库。我想问一下,如果有人对如何解决这个问题有一些想法。也许在另一个xUnit实现中已经实现了这个问题的解决方案。

You can avoid integration test conflicts 2 ways:

您可以通过两种方式避免集成测试冲突:

  • running only those tests parallel, which uses very different tables of your database, so they don't conflict
  • 只运行那些并行的测试,它们使用非常不同的数据库表,因此它们不会发生冲突
  • create a new database for conflicting tests
  • 为冲突的测试创建一个新的数据库

Ofc. you can combine these 2 solutions. I don't know about any phpunit test runner which supports any of these approaches, so I think you have to write your own test runner to speed up the process... Btw you can still group your integration tests, and run only a few of them at once, if you are using them by development...

OFC。你可以结合这两种解决方案。我不知道任何支持这些方法的phpunit测试运行器,所以我认为你必须编写自己的测试运行器来加速这个过程...顺便说一句,你仍然可以对集成测试进行分组,并且只运行一些如果你是通过开发使用它们的话

Be aware, that the same conflicts can cause concurrency issues under heavy loading in PHP. For example if you lock 2 files in reverse order under 2 separate controller action, then your application can end up in a deadlock... I am seeking a way to test concurrency issues in PHP, but no luck so far. I don't have time currently to write my own solution, and I am not sure I can manage it, it's pretty hard stuff... :S

请注意,相同的冲突可能会导致PHP负载繁重时出现并发问题。例如,如果您在2个单独的控制器操作下以相反的顺序锁定2个文件,那么您的应用程序可能会陷入死锁...我正在寻找一种方法来测试PHP中的并发问题,但到目前为止还没有运气。我目前没有时间编写我自己的解决方案,而且我不确定我能管理它,这是非常难的......:S

#3


0  

There is also this awesome library (fastest) for executing tests in parallel. It is optimized for functional/integration tests, giving an easy way to work with N databases in parallel.

还有这个非常棒的库(最快)可以并行执行测试。它针对功能/集成测试进行了优化,提供了一种并行处理N个数据库的简便方法。

Our old codebase run in 30 minutes, now in 7 minutes with 4 Processors.

我们的旧代码库运行30分钟,现在用4个处理器运行7分钟。

Features

  • Functional tests could use a database per processor using the environment variable.
  • 功能测试可以使用环境变量为每个处理器使用数据库。
  • Tests are randomized by default.
  • 默认情况下,测试是随机的。
  • Is not coupled with PhpUnit you could run any command.
  • 没有与PhpUnit结合,你可以运行任何命令。
  • Is developed in PHP with no dependencies.
  • 是在PHP中开发的,没有依赖项。
  • As input you could use a phpunit.xml.dist file or use pipe.
  • 作为输入,您可以使用phpunit.xml.dist文件或使用管道。
  • Includes a Behat extension to easily pipe scenarios into fastest.
  • 包括Behat扩展,可轻松将场景切换为最快。
  • Increase Verbosity with -v option.
  • 使用-v选项增加详细程度。

Usage

find tests/ -name "*Test.php" | ./bin/fastest "bin/phpunit -c app {};"

find tests / -name“* Test.php”| ./bin/fastest“bin / phpunit -c app {};”

#1


4  

This is a good question: preparing for parallel unit tests is going to require learning some new Best Practices, and I suspect some of them are going to slow our tests down.

这是一个很好的问题:准备并行单元测试需要学习一些新的最佳实践,我怀疑它们中的一些将会减慢我们的测试速度。

At the highest level, the advice is: avoid testing with a database wherever possible. Abstract all interactions with your database, and then mock that class. But you've already noted your question is about integration tests, where this is not possible.

在*别,建议是:尽可能避免使用数据库进行测试。摘要与数据库的所有交互,然后模拟该类。但是你已经注意到你的问题是关于集成测试,这是不可能的。

When using PDO, I generally use sqlite::memory: Each test gets its own database. It is anonymous and automatically cleaned up when the test ends. (But I noted some problems with this when you real application is not using sqlite: Suggestions to avoid DB deps when using an in-memory sqlite DB to speed up unit tests )

使用PDO时,我通常使用sqlite :: memory:每个测试都有自己的数据库。它是匿名的,并在测试结束时自动清理。 (但是当你真正的应用程序没有使用sqlite时,我注意到了一些问题:当使用内存中的sqlite DB来加速单元测试时,建议避免使用DB deps)

When using a database that does not have an in-memory choice, create the database with a random name. If the parallelization is at the PHPUnit process level, quite coarse, you could use the process pid. But that has no real advantages over a random name. (I know PHP is single-threaded, but perhaps in future we would have a custom phpUnit module, that uses threads to run tests in parallel; we might as well be ready for that.)

使用没有内存选择的数据库时,请使用随机名称创建数据库。如果并行化处于PHPUnit进程级别,非常粗略,则可以使用进程pid。但这比随机名称没有真正的优势。 (我知道PHP是单线程的,但是将来我们可能会有一个自定义的phpUnit模块,它使用线程并行运行测试;我们也可以为此做好准备。)

If you have the xUnit Test Patterns book, chapter 13 is about testing databases (relatively short). Chapters 8 and 9 on transient vs. persistent fixtures are useful too. And, of course, most of the book is on abstraction layers to make mocking easier :-)

如果你有xUnit测试模式书,第13章是关于测试数据库(相对较短)。关于瞬态与持续固定装置的第8章和第9章也很有用。而且,当然,本书的大部分内容都是关于抽象层,以使模拟变得更容易:-)

#2


0  

But there remains the problem with integration tests, or, more generally, tests that interact with DB. For the sake of consistency, the testDB has to be resetted and fixtures loaded after each test. But in parallel tests there is a problem with race conditions, because all processes use the same DB.

但是,集成测试仍然存在问题,或者更常见的是与DB交互的测试。为了保持一致性,必须重置testDB并在每次测试后加载夹具。但在并行测试中,竞争条件存在问题,因为所有进程都使用相同的DB。

So to be able to run integration tests in parallel, we have to assign own database to each process. I would like to ask, if someone has some thoughts about how this problem can be solved. Maybe there are already implemented solutions to this problem in another xUnit implementation.

因此,为了能够并行运行集成测试,我们必须为每个进程分配自己的数据库。我想问一下,如果有人对如何解决这个问题有一些想法。也许在另一个xUnit实现中已经实现了这个问题的解决方案。

You can avoid integration test conflicts 2 ways:

您可以通过两种方式避免集成测试冲突:

  • running only those tests parallel, which uses very different tables of your database, so they don't conflict
  • 只运行那些并行的测试,它们使用非常不同的数据库表,因此它们不会发生冲突
  • create a new database for conflicting tests
  • 为冲突的测试创建一个新的数据库

Ofc. you can combine these 2 solutions. I don't know about any phpunit test runner which supports any of these approaches, so I think you have to write your own test runner to speed up the process... Btw you can still group your integration tests, and run only a few of them at once, if you are using them by development...

OFC。你可以结合这两种解决方案。我不知道任何支持这些方法的phpunit测试运行器,所以我认为你必须编写自己的测试运行器来加速这个过程...顺便说一句,你仍然可以对集成测试进行分组,并且只运行一些如果你是通过开发使用它们的话

Be aware, that the same conflicts can cause concurrency issues under heavy loading in PHP. For example if you lock 2 files in reverse order under 2 separate controller action, then your application can end up in a deadlock... I am seeking a way to test concurrency issues in PHP, but no luck so far. I don't have time currently to write my own solution, and I am not sure I can manage it, it's pretty hard stuff... :S

请注意,相同的冲突可能会导致PHP负载繁重时出现并发问题。例如,如果您在2个单独的控制器操作下以相反的顺序锁定2个文件,那么您的应用程序可能会陷入死锁...我正在寻找一种方法来测试PHP中的并发问题,但到目前为止还没有运气。我目前没有时间编写我自己的解决方案,而且我不确定我能管理它,这是非常难的......:S

#3


0  

There is also this awesome library (fastest) for executing tests in parallel. It is optimized for functional/integration tests, giving an easy way to work with N databases in parallel.

还有这个非常棒的库(最快)可以并行执行测试。它针对功能/集成测试进行了优化,提供了一种并行处理N个数据库的简便方法。

Our old codebase run in 30 minutes, now in 7 minutes with 4 Processors.

我们的旧代码库运行30分钟,现在用4个处理器运行7分钟。

Features

  • Functional tests could use a database per processor using the environment variable.
  • 功能测试可以使用环境变量为每个处理器使用数据库。
  • Tests are randomized by default.
  • 默认情况下,测试是随机的。
  • Is not coupled with PhpUnit you could run any command.
  • 没有与PhpUnit结合,你可以运行任何命令。
  • Is developed in PHP with no dependencies.
  • 是在PHP中开发的,没有依赖项。
  • As input you could use a phpunit.xml.dist file or use pipe.
  • 作为输入,您可以使用phpunit.xml.dist文件或使用管道。
  • Includes a Behat extension to easily pipe scenarios into fastest.
  • 包括Behat扩展,可轻松将场景切换为最快。
  • Increase Verbosity with -v option.
  • 使用-v选项增加详细程度。

Usage

find tests/ -name "*Test.php" | ./bin/fastest "bin/phpunit -c app {};"

find tests / -name“* Test.php”| ./bin/fastest“bin / phpunit -c app {};”