代码之家  ›  专栏  ›  技术社区  ›  user254875486 TM Creative

如何测试MySQL事务?

  •  13
  • user254875486 TM Creative  · 技术社区  · 14 年前

    我有一个关于测试事务中查询的问题。我使用MySQL事务已经有一段时间了,每当我这样做时,我都会使用类似的东西:

    $doCommit = true;
    $error = "";
    mysql_query("BEGIN");
    
    /* repeat this part with the different queries in the transaction
       this often involves updating of and inserting in multiple tables */
    $query = "SELECT, UPDATE, INSERT, etc";
    $result = mysql_query($query);
    if(!$result){
        $error .= mysql_error() . " in " . $query . "<BR>";
        $doCommit = false;
    }
    /* end of repeating part */
    
    if($doCommit){
        mysql_query("COMMIT");
    } else {
        echo $error;
        mysql_query("ROLLBACK");
    }
    

    现在,我经常想测试我的交易,所以我改变了 mysql_query("COMMIT"); mysql_query("ROLLBACK"); 但是我可以想象这不是一个很好的测试方法。将每个表复制到临时表中,然后更新并插入到这些表中,然后删除它们(例如,因为表可能非常大),这通常是不可行的。当然,当代码投入生产时,相关的错误处理(而不仅仅是打印错误)就开始了。

    做这种事最好的方法是什么?

    3 回复  |  直到 14 年前
        1
  •  10
  •   ircmaxell    14 年前

    首先,您的实现中有一个bug。如果查询出错,当前事务将自动回滚,然后关闭。因此,当您继续执行查询时,它们将不在事务中(它们将提交给数据库)。然后,当你执行 Rollback ,它将无声地失败。从 MySQL docs :

    Rolling back can be a slow operation that may occur implicitly without the user 
    having explicitly asked for it (for example, when an error occurs).
    

    显式命令 ROLLBACK 仅当您在应用程序中确定需要回滚时才应使用(因为查询错误以外的原因)。例如,如果您从一个帐户中扣除资金,如果您发现用户没有足够的资金来完成交换,您将显式回滚…

    至于测试事务,我确实复制了数据库。我创建了一个新的数据库并安装了一组“虚拟数据”。然后我使用自动化工具运行所有测试。该工具将实际提交事务并强制回滚,并检查在整个测试过程中是否保持了预期的数据库状态。由于如果事务的输入未知,则很难通过编程知道事务的结束状态,因此测试活动数据(甚至从活动数据复制数据)并不容易。您可以这样做(并且应该这样做),但不要依赖这些结果来确定您的系统是否工作。使用这些结果为自动化测试人员构建新的测试用例…

        2
  •  3
  •   JustAMartin    14 年前

    也许您可以重构第一个示例并使用一些DB访问包装类?

    在这个包装类中,您可以有一个变量$normalcommit=true; 以及设置$normalCommit变量的方法setCommitmode()。 还有一个方法commit(),如果($normalCommit==true),它将提交。 或者甚至有一个变量$failtransaction,它调用mysql_query(“rollback”);如果您愿意的话(这样您可以通过/失败许多顺序测试)。

    然后,运行测试时,可以在测试代码文件中的某个位置设置: $mydbclass->setcommitmode(假); 或 $mydbclass->RollbackNextOperation(true); 在你希望失败的操作之前,它只会失败。这样,您正在测试的代码将不包含那些失败/提交检查,只有db类将包含它们。

    通常,只有测试代码(尤其是在进行单元测试的情况下)才应该调用这些setCommitmode和rollbackNextOperation方法,因此您不小心将这些调用留在生产代码中。

    或者,您可以将一些疯狂的数据传递给您的方法(如果您正在测试一个方法),比如将负变量保存到无符号字段中,然后,如果您的代码在这种SQL错误之后没有提交(但它不应该提交),则事务应该100%失败。

        3
  •  2
  •   mhitza Federico Taschin    14 年前

    通常我使用类似的东西(我的示例使用PDO):

    $db->beginTransaction();
    try {
      $db->exec('INSERT/DELETE/UPDATE');
      $db->commit();
    }
    catch (PDOException $e) {
      $db->rollBack();
      // rethrow the error or
    }
    

    或者,如果您有自己的异常处理程序,请为pdoExceptions使用一个特殊的子句,在其中回滚执行。例子:

    function my_exception_handler($exception) {
      if($exception instanceof PDOException) {
        // assuming you have a registry class
        Registry::get('database')->rollBack();
      }
    }