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

hquas 发布于 2019-03-09 mstest 最后更新 2019-03-09 14:37 0 浏览

我正在处理一个非常奇怪的问题。最初,我认为这是一个清理测试数据的问题......但是在完全重构我的测试数据清理代码并仍然看到完全相同的行为之后......我不知所措。 我有不同类别的245个单元测试方法。每个类都有自己独特的测试数据,并初始化这些对象,然后在每种测试方法中,通常将数据插入到数据库中,然后使用测试进行处理。每个测试类都有一个ClassCleanup方法,用于清除数据库中的所有测试数据,并且该ClassCleanup也在TestInitialize上运行,以确保在运行任何其他测试方法之前清除所有内容。 当我使用VS 2012测试浏览器“全部运行”时,22个测试失败。它们都会因主键约束违规的一些变化而失败。这意味着,当他们为这些测试初始化​​数据时,数据不会从该类的先前测试方法中清除。如果我重新运行所有测试,那么每次都会遇到相同的测试失败。这是相当可重复的。无论我运行多少次测试。这些相同的27个测试失败,主键违规。 然而,奇怪的是,如果我只重新运行那些失败的测试,只有9个测试失败。这也是可重复的,也就是说,不管我只运行多少次这27次以前的失败测试,​​14次都会失败,其余的将会通过。这继续,因为我只运行失败的测试,直到我没有一个测试失败。还应该注意的是,如果我单独运行每个测试课程,一切都会通过。 我知道这看起来如何。 “你显然没有清理你的测试数据。”如果是这样的话,那么无论发生什么,我都应该看到相同的测试在每次运行中都会失败。这27个测试应该会失败,而不仅仅是当我运行其他任何东西时。 “在类之间不能有唯一的主键,并且事物没有被清理。”往上看。即使我在我的类中重复了主键(我不这样做,因为我亲自对各个类中的测试数据上的主键的唯一性进行了三重检查),因为这些测试不是在单独的线程上同时运行(已通过记录ThreadId进行验证),则无论如何,任何给定测试的清除代码都会清除重复的数据。 “你一定不能使用连接池。”不,实际上我是。而且我已经使用SQL Profiler验证了这些请求是绝对合并的。另外,因为这些测试不是并行运行的,所以只有一个连接线程。 “你不应该使用连接池。”嗯,是的,我应该,因为底层代码库支持各种Web项目,但为了争论,我试着运行禁用连接池的所有测试(在连接字符串中使用Pooling = false),并获得完全相同的结果。无论如何,行为没有变化。 “你的当地环境肯定有问题。”在其他同事的开发盒(偶尔使用SQL 2012)上运行这些测试的结果也一样。这不是我的环境,甚至是我的SQL Server版本所独有的。 “你应该尝试从命令行运行mstest。”已经做到了。相同的结果。 如果有人遇到过这样的事情,请告诉我。我知道一定有一些我很想念的东西,因为这种问题通常就是这种情况,但我已经尽可能多地介绍了解决这个问题的方法。

已邀请:

mqui

赞同来自:

以下是基于您的数据库处于完全恢复模式并且在测试期间不执行任何恢复或其他欺骗(例如分离/重新附加数据库等)的假设。 这是一个相当繁琐的方法来调查您的问题,但保证提供所需的数据来解决这个问题。

  1. 对数据库进行完全备份在启动测试套件之前,请执行此操作。我们将恢复数据库,以确保您有足够的磁盘空间来存储2-3份数据库文件。
  2. 创建Sql Profiler跟踪对于事件,选择RPC Starting / completed,sql Batch Starting / completed,Sql Statement启动/完成,SP语句启动/完成,TM:* completed,SQLTransaction,DTCTransaction和用户错误消息。捕获所有列。
  3. 重现问题运行最少数量的测试以产生故障。让测试完成,以便捕获所有清理代码,然后停止探查器跟踪。
  4. 进行事务日志备份我们可能需要稍后进行时间点恢复。
  5. 在跟踪中找到故障如果您遇到主键故障,那么应该很容易跟踪,只需查找用户错误消息。记下错误发生的确切时间。
  6. 检查跟踪是否存在明显问题从错误开始并向后工作,直到找到失败的测试开始。记下上次失败测试的设置开始的确切时间。检查此范围内的所有sql。 sql正是你期望的吗?行数是否正确? 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具有改进的数据库还原向导,使得时间点恢复非常容易。祝你好运!

iid

赞同来自:

不知道为什么你的失败是这样但我有类似的东西,现在我把transactionscope放在设置中,如下所示:

public void SetUp()
{
 _transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew);
}
并将其丢弃在拆解中。这摆脱了我的数据库问题,并阻止我编写手动清理代码。