MSTest单元测试(VS 2012)与SQL Server,间歇性主键冲突

Fin*_*ter 5 sql-server mstest sql-server-2008 visual-studio-2012

我正在处理一个非常奇怪的问题.最初,我认为这是清理测试数据的问题......但是在完全重构我的测试数据清理代码并仍然看到完全相同的行为之后......我很茫然.

我有各种类别的245个单元测试方法.每个类都有自己独特的测试数据,我初始化这些对象,然后在每个测试方法中,数据通常被插入到数据库中,然后通过测试进行操作.每个测试类都有一个ClassCleanup方法,用于清除数据库中的所有测试数据,ClassCleanup也在TestInitialize上运行,以确保在运行任何其他测试方法之前清除所有内容.

当我使用VS 2012测试资源管理器"全部运行"时,22个测试失败.它们都因主键约束违规的某些变化而失败.这意味着,当他们为这些测试初始化​​数据时,数据没有从该类中的先前测试方法中清除.如果我重新运行所有测试,我每次都会失败.这是相当可重复的.无论我运行多少次测试.这些相同的27个测试因主键违规而失败.

然而,奇怪的是,如果我只重新运行那些失败的测试,那么只有9个测试失败.这也是可重复的,这意味着,无论我只运行了多少次这27个先前失败的测试,14个将失败,其余的将通过.这继续,因为我只运行失败的测试,直到我达到一个没有测试失败的点.还应该注意的是,如果我单独运行每个测试类,一切都会通过.

我知道这看起来如何.

"你显然没有清理你的测试数据." 如果是这种情况,那么我应该看到每次运行都会失败,无论如何.那些27次测试每次运行都会失败,而不仅仅是当我运行其他所有内容时.

"你不能在类之间拥有唯一的主键,而且事情没有得到清理." 往上看.即使我在我的课程中重复了主键(我没有,因为我亲自三重检查了各个类中测试数据的主键的唯一性),因为这些测试不是在不同的线程上并发运行的(已经通过记录ThreadId验证了,任何给定测试的清理代码都会清除重复的数据,无论如何.

"你不能使用连接池." 不,实际上我是.我已经使用SQL事件探查器验证了请求肯定是池化的.此外,由于这些测试并非并行运行,因此只有一个连接线程.

"你不应该使用连接池." 嗯,是的,我应该,因为底层代码库支持各种Web项目,但为了参数我尝试运行禁用连接池的所有测试(在连接字符串中使用Pooling = false),我得到完全相同的结果.行为没有任何改变.

"你的当地环境一定有问题." 我在其他同事的开发盒(顺便使用SQL 2012)上运行这些测试也得到了相同的结果.这不是我的环境,甚至是我的SQL Server版本所特有的.

"你应该尝试从命令行运行mstest." 已经这样做了.结果相同.

如果有人遇到过这样的事情,请告诉我.我知道必须有一些简单的我缺少,因为这种问题通常就是这种情况,但我已经尽可能多地考虑了这个问题.

Str*_*DBA 2

以下内容基于这样的假设:您的数据库处于完全恢复模式,并且您在测试期间不执行任何恢复或其他欺骗(例如分离/重新附加数据库等)。

这是一个相当乏味的方法来调查您的问题,但保证提供解决这个问题所需的数据。

  1. 对数据库进行完整备份 在启动测试套件之前执行此操作。我们将恢复数据库,因此还要确保您有足够的磁盘空间来容纳 2-3 个数据库文件的副本。

  2. 创建 Sql Profiler 跟踪对于事件,选择 RPC 开始/完成、sql 批处理开始/完成、Sql 语句开始/完成、SP 语句开始/完成、TM:* 完成、SQLTransaction、DTCTransaction 和用户错误消息。捕获所有列。

  3. 重现问题 运行最少数量的测试以产生失败。让测试完成,以便捕获所有清理代码,然后停止探查器跟踪。

  4. 进行事务日志备份 我们可能需要它来进行稍后的时间点恢复。

  5. 在跟踪中查找故障 如果您遇到主键故障,那么应该很容易追踪,只需查找用户错误消息即可。记下错误发生的确切时间。

  6. 检查跟踪是否存在明显问题 从错误开始并向后工作,直到找到失败的测试的开始位置。记下上次失败的测试开始设置的确切时间。检查这个范围内的所有sql。sql是否正是您所期望的?行数是否正确?transactionId是否正确?(对于不在事务中的每个语句,transactionId 列应该不同,而对于事务内的每个语句,transactionId 列应该相同)。如果您的 BEGIN TRAN/COMMIT TRAN/ROLLBACK TRAN 不匹配,则 transactionId 会通知您。

  7. 将数据库恢复到失败的测试设置之前 将其恢复到新数据库,以便我们可以比较原始数据库和副本。首先使用“RESTORE DATABASE ....WITH NORECOVERY”恢复完整备份。然后使用“RESTORE LOG .. WTIH STOPAT, RECOVERY”恢复事务日志备份,并指定失败的测试设置之前的时间。

  8. 验证数据库状态 检查可能尚未清理的测试数据。一切都是应该的吗?如果没有,您可以将数据库再次恢复到较早的时间点。您正在寻找测试开始之前数据库处于良好、已知状态的时间点。

  9. 将数据库恢复到错误发生之前的位置如果有空间,请恢复到另一个新数据库。检查导致 PK 违规的数据。如果再次运行有问题的语句会出现错误吗?验证它是否发生。

    • 如果没有发生,您的问题可能是事务处理不匹配。如果您之前错过了 COMMIT,则可能还有一个事务处于打开状态。当您使用 STOPAT 进行恢复时,任何未提交的事务都将回滚。这也可以解释如何单独运行测试,但一起运行它们会失败。
    • 如果确实发生,则向后工作,直到找到问题。您可能需要多次恢复数据库才能弄清楚。您的过程将是恢复数据库、检查跟踪、检查数据、恢复到不同点、检查跟踪、检查数据等。
  10. 如果在所有这些之后您仍然不知所措,那么您可能需要研究使用数据库快照作为单元测试的一部分。基本上,创建数据库快照,设置并运行测试,拆卸被替换为将数据库恢复到快照。这将保证每次测试之前和之后都有相同的数据库。

2012 Management Studio 具有改进的数据库还原向导,使时间点还原变得非常容易。 祝你好运!