简体   繁体   English

集成测试中的并行 PHPUnit 测试

[英]Parallel PHPUnit testing in integration tests

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 .随着运行完整的 PHPUnit 套件所需时间的增加,我们的团队开始怀疑是否有可能并行运行单元测试 Recently I read an article about Paraunit, also Sebastian Bergman wrote, he'll add parallelism into PHPUnit 3.7.最近我读了一篇关于 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.为了保持一致性,每次测试后都必须重置 testDB 并加载夹具。 But in parallel tests there is a problem with race conditions, because all processes use the same DB .但是在并行测试中存在竞争条件问题,因为所有进程都使用相同的 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.在我的团队中,我们使用 MongoDB,因此一种解决方案是以编程方式为每个 PHPUnit 进程创建一个配置文件,并使用生成的数据库名称(对于此进程),并且在setUp()方法中,我们可以将主 TestDb 克隆到这个临时文件中. But before we start to implement this approach I would like to ask for your ideas about the topic.但在我们开始实施这种方法之前,我想询问您对这个主题的想法。

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.在使用 PDO 时,我一般使用 sqlite::memory:每个测试都有自己的数据库。 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 ) (但是当您真正的应用程序不使用 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.如果并行化是在 PHPUnit 进程级别,非常粗糙,您可以使用进程 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.) (我知道 PHP 是单线程的,但也许将来我们会有一个自定义的 phpUnit 模块,它使用线程并行运行测试;我们不妨为此做好准备。)

If you have the xUnit Test Patterns book, chapter 13 is about testing databases (relatively short).如果你有 xUnit Test Patterns 一书,第 13 章是关于测试数据库的(相对较短)。 Chapters 8 and 9 on transient vs. persistent fixtures are useful too.关于瞬态与持久性装置的第 8 章和第 9 章也很有用。 And, of course, most of the book is on abstraction layers to make mocking easier :-)而且,当然,这本书的大部分内容都是关于抽象层以使模拟更容易:-)

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 {};"

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.为了保持一致性,每次测试后都必须重置 testDB 并加载夹具。 But in parallel tests there is a problem with race conditions, because all processes use the same 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...我不知道任何支持这些方法的 phpunit 测试运行器,所以我认为你必须编写自己的测试运行器来加速这个过程......顺便说一句,你仍然可以对你的集成测试进行分组,并且只运行几个如果您在开发中使用它们,请立即使用它们...

Be aware, that the same conflicts can cause concurrency issues under heavy loading in PHP.请注意,在 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.例如,如果您在 2 个单独的控制器操作下以相反的顺序锁定 2 个文件,那么您的应用程序可能会陷入死锁……我正在寻找一种方法来测试 PHP 中的并发问题,但到目前为止还没有运气。 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我目前没有时间编写自己的解决方案,而且我不确定我是否可以管理它,这是非常困难的事情......:S

In case that your application is coupled with a specific vendor eg.如果您的应用程序与特定供应商耦合,例如。 postgresql you can create separate stacks with docker and docker-compose. postgresql 您可以使用 docker 和 docker-compose 创建单独的堆栈。 Then group together tests by purpoce eg.然后按目的将测试分组在一起,例如。 model tests, controller tests etc etc.模型测试、控制器测试等。

For each group deploy in your pipeline a specific stack using docker-compos and run the tests via docker.对于您的管道中的每个组,使用 docker-compos 部署一个特定的堆栈并通过 docker 运行测试。 The idea is to have seperate environment with seperate databases hence you avoid the confict.这个想法是有单独的环境和单独的数据库,因此你可以避免冲突。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM