2012-04-26 137 views
14

我得到一个头痛中总是插入夹具之前运行TRUNCATE没有第一设置外键检查关PHPUnit的行为:的PHPUnit和MySQL截断误差

Syntax error or access violation: 1701 Cannot truncate a table referenced in a foreign key constraint

基本上,PHPUnit的尝试截断在插入灯具之前的表格。我如何把它告诉SET FOREIGN_KEY_CHECKS=0;

+0

发生这种情况即使在引用表格中你截断表是空的。很确定这是一个MySQL错误,无论这里的评论如何: http://bugs.mysql.com/bug.php?id=54678 @Tower解决方案是可悲的唯一选择。 – jmc 2014-04-19 10:44:11

回答

27

我发现似乎的答案。我最终通过扩展一个类来覆盖一些方法。

<?php 

/** 
* Disables foreign key checks temporarily. 
*/ 
class TruncateOperation extends \PHPUnit_Extensions_Database_Operation_Truncate 
{ 
    public function execute(\PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, \PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) 
    { 
     $connection->getConnection()->query("SET foreign_key_checks = 0"); 
     parent::execute($connection, $dataSet); 
     $connection->getConnection()->query("SET foreign_key_checks = 1"); 
    } 
} 

然后使用例子:

class FooTest extends \PHPUnit_Extensions_Database_TestCase 
{ 
    public function getSetUpOperation() 
    { 
     $cascadeTruncates = true; // If you want cascading truncates, false otherwise. If unsure choose false. 

     return new \PHPUnit_Extensions_Database_Operation_Composite(array(
      new TruncateOperation($cascadeTruncates), 
      \PHPUnit_Extensions_Database_Operation_Factory::INSERT() 
     )); 
    } 
} 

所以我有效地禁用外键检查和设置他们回来,如果他们有没有设置。显然,你应该创建一个具有此功能的基类,并且扩展它而不是PHPUnit的TestCase。

+0

你在哪里设置外键检查? – Jeune 2012-05-23 06:06:21

+0

@June它在TruncateOperation-> execute中重置,第三行,它在那里:“SET foreign_key_checks = 1”。在git上发现了这个问题,基本上也是这样,并附加一些解释:[DbUnit GIT](https://gist.github.com/1319731) – qrazi 2012-11-27 14:43:36

+0

很好的解决方案。我很想使用它,但作为一个控制怪胎,我宁愿确保数据输入正确与放宽FK约束,所以我只是在'setUp'方法中添加了一些自定义删除查询,然后再调用'parent :: setUp',并且工作正常。 – 2013-03-01 18:37:36

3

或者,您可以通过删除所有记录并重新设置自动增加计数器的组合来模拟截断。

首先,您将不得不创建新的数据库操作类来处理重置表的自动增量值。如上所述

/** 
* Resets all AUTO_INCREMENT counters on all tables in a dataset. 
* @see PHPUnit_Extensions_Database_Operation_IDatabaseOperation 
*/ 
class ResetAutoincrementOperation implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation 
{ 

    /* 
    * @see PHPUnit_Extensions_Database_Operation_IDatabaseOperation::execute() 
    */ 
    public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, 
      PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) 
    { 
     foreach ($dataSet->getReverseIterator() as $table) { 
      $query = "ALTER TABLE {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())}" 
       . " AUTO_INCREMENT = 1 
      "; 

      try { 
       $connection->getConnection()->query($query); 
      } catch (PDOException $e) { 
       throw new PHPUnit_Extensions_Database_Operation_Exception('RESET_AUTOINCREMENT', 
         $query, array(), $table, $e->getMessage()); 
      } 
     } 
    } 
} 

现在覆盖getSetUpOperation()getTearDownOperation()方法。

class FooTest extends PHPUnit_Extensions_Database_TestCase 
{ 
    /** 
    * @see PHPUnit_Extensions_Database_TestCase::getTearDownOperation() 
    */ 
    protected function getTearDownOperation() 
    { 
     // Clean up after ourselves 
     return new PHPUnit_Extensions_Database_Operation_Composite(array(
      PHPUnit_Extensions_Database_Operation_Factory::DELETE_ALL(), // 1. delete all records from table 
      new ResetAutoincrementOperation() // 2. reset auto increment value 
     )); 
    } 

    /** 
    * @see PHPUnit_Extensions_Database_TestCase::getSetUpOperation() 
    */ 
    protected function getSetUpOperation() 
    { 
     return new PHPUnit_Extensions_Database_Operation_Composite(array(
       PHPUnit_Extensions_Database_Operation_Factory::DELETE_ALL(), // 1. delete all records from table 
       new ResetAutoincrementOperation(), // 2. reset auto increment value 
       PHPUnit_Extensions_Database_Operation_Factory::INSERT() // 3. insert new records 
     )); 
    } 
} 

为我工作与MySQL 5.5.24和3.6.10的PHPUnit