Php Symfony2中的数据库测试实践?如何隔离?

Php Symfony2中的数据库测试实践?如何隔离?,php,unit-testing,symfony,Php,Unit Testing,Symfony,目前使用Symfony2测试数据库交互的最佳实践是什么?我有一个简单的积垢设置,我想确保我的测试是正常的。现在,我有4个测试,每个测试都确保创建、更新、删除和列出操作正常进行 在我的测试用例中,我有两种神奇的方法,构造和析构函数。在它们内部,我使用'php app/console…'调用exec(),以便创建数据库、创建模式,然后删除数据库。然而,这是非常缓慢的,而且当我有多个测试用例时,它总是发生 当涉及到数据库测试和隔离此类测试时,我应该如何进行?数据库测试总是很慢,因为您需要在每次测试之前

目前使用Symfony2测试数据库交互的最佳实践是什么?我有一个简单的积垢设置,我想确保我的测试是正常的。现在,我有4个测试,每个测试都确保创建、更新、删除和列出操作正常进行

在我的测试用例中,我有两种神奇的方法,构造和析构函数。在它们内部,我使用'php app/console…'调用exec(),以便创建数据库、创建模式,然后删除数据库。然而,这是非常缓慢的,而且当我有多个测试用例时,它总是发生


当涉及到数据库测试和隔离此类测试时,我应该如何进行?

数据库测试总是很慢,因为您需要在每次测试之前/之后创建/删除模式。为避免不必要的操作,您可以:

  • 在“setUpBeforeClass”方法中创建架构
  • 确保您的4个测试在一个带有'@depend'注释的线程中启动
  • 在“tearDownAfterClass”方法中删除架构
对于您的测试用例,架构将只创建/删除一次

您还可以设置条令以使用inmemory sqlite数据库(速度非常快):


无论如何,“_-construct”和“_-destruct”不应该在phpunit测试用例中使用,相反,您应该使用“setUp”和“tearDown”。

我认为最好总是从干净开始,以确保测试完全隔离。为此,我只是在每次测试之前构建数据库结构,然后用给定测试所需的装置填充它

请注意,我只构建所需的数据库表,并且只插入所需的装置。这比加载一个大的数据库转储要快一点。它也更干净,因为测试不共享夹具(这使它们更易于维护)

我有一个名为KernelAwareTest的基本测试用例类,它帮助我构建模式。你可以在gist上找到它:

setUp()
引导Symfony内核并将对它的引用存储在类属性中(以及对DIC和实体管理器的引用)。还可以调用
generateSchema()
,以生成数据库模式(它使用来自Doctrine的模式工具)

默认情况下,它为实体管理器已知的所有实体生成数据库结构。通过重写
getMetadatas()
方法,可以在测试类中更改此行为


附言:我尝试使用内存数据库(sqlite),但并不完美。无论如何,对生产中使用的数据库运行测试可能更好。

这个问题已经很老了,但今天仍然有效,下面是我的经验以及我今天在Symfony项目中如何处理它

我开始在测试中使用SQLite内存数据库,并在每个测试用例之前重建db schema+插入的fixture。这有两个主要缺点:

  • 还是太慢了:(
  • 在dev&prod上,我使用了Mysql,这很快就成了一个问题,因为SQLite没有所需的所有特性,有时行为也会有所不同
使用MSQL进行测试和重建模式+在每次测试之前插入夹具实在太慢了。所以我正在寻找替代方案

我偶然发现了这篇博文:

这里的家伙建议在活动数据库事务中运行测试,并在每次测试后简单地回滚任何更改

我接受了这个想法,并为此创建了一个捆绑包:

捆绑包的设置非常简单,不需要更改任何现有的php测试类。在内部,它更改了doctrine配置以使用自定义数据库连接+驱动程序

使用此捆绑包,您只需在运行整个testsuite之前创建一次数据库模式+插入装置(我更喜欢在自定义phpunit引导文件中执行此操作)。使用phpunit侦听器,所有测试都将在数据库事务中运行

我已经使用这个捆绑包一段时间了,对我来说,使用SQLite、MySQL或PostgreSQL就可以很好地解决这个问题


从一段时间以来,它也被用于symfony演示项目。

在本地机器上测试是一件痛苦的事,所以我开始使用ci system buddy.works(有免费的单机版),为此我需要自己解决这个问题

结果是:

  • 所有测试都有效
  • 测试正在生产sql数据上运行
  • 测试是分开运行的(不是在开发或生产环境中),所以我可以这样做 我想要什么数据库
  • 所有对git的推送都经过测试,如果有什么东西坏了,我会收到通知
  • 部署分支的所有推/拉请求都会自动上载到生产
这是我的解决方案:

  • 配置中的second parameters.yml和测试配置
  • 在生产中,我每天都在做sqldump
  • 在ci上启动测试时,通过scp将此sql备份复制到测试计算机
  • 为了准备所有这些,我使用robo.li(我的配置如下)
  • /**
    *这是Robo task runner的项目控制台命令配置。
    *
    *@见http://robo.li/
    */
    类RoboFile扩展\Robo\Tasks
    {
    函数loadDb(){
    $this->taskExecStack()
    ->stopOnFail()
    ->exec(“mysql-h mariadb-u root-pqwerty-e‘创建数据库测试’”)
    ->exec(“mysql-h mariadb-u root-pqwerty testrun();
    }
    函数prepareDb(){
    $this->taskExecStack()
    ->stopOnFail()
    ->exec(“cp-app/config/parameters-test.yml-app/config/parameters.yml”)
    ->run();
    $this->taskreplaceinfle('app/config/parameters.yml'))
    ->from('数据库\主机:127.0.0.1')
    ->至(“数据库_主机:'mariadb'”)
    ->run();
    $this->taskreplaceinfle('app/config/parameters.yml'))
    ->from('database_user:dbuser'))
    ->至(“数据库用户:'root'))
    ->run();
    $this->taskreplaceinfle('app/config
    
    doctrine:
        dbal:
            driver: pdo_sqlite
            path: :memory:
            memory: true
    
    /**
    * This is project's console commands configuration for Robo task runner.
    *
    * @see http://robo.li/
    */
    class RoboFile extends \Robo\Tasks
    {
    
    function  loadDb(){
        $this->taskExecStack()
            ->stopOnFail()
            ->exec(" mysql -h mariadb -u root -pqwerty -e 'create database test' ")
            ->exec(" mysql -h mariadb -u root -pqwerty test < test.sql ")
            ->run();
    }
    
    
    function prepareDb(){
        $this->taskExecStack()
            ->stopOnFail()
            ->exec("cp  app/config/parameters-test.yml app/config/parameters.yml")
            ->run();
    
        $this->taskReplaceInFile('app/config/parameters.yml')
            ->from('database_host:     127.0.0.1')
            ->to("database_host:     'mariadb'")
            ->run();
    
        $this->taskReplaceInFile('app/config/parameters.yml')
            ->from('database_user:     dbuser')
            ->to("database_user:     'root'")
            ->run();
    
        $this->taskReplaceInFile('app/config/parameters.yml')
            ->from('database_password: 123')
            ->to("database_password: 'qwerty'")
            ->run();
    
    
    }