5

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

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

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

然而,奇怪的是,如果我只重新运行那些失败的测试,只有 9 个测试失败。这也是可重现的,这意味着无论我只运行这 27 个先前失败的测试多少次,14 个将失败,其余的将通过。当我只运行失败的测试时,这种情况会继续下去,直到我达到没有一个测试失败的地步。还应该注意的是,如果我单独运行每个测试类,一切都会通过。

我知道这看起来如何。

“你显然没有清理你的测试数据。” 如果是这种情况,那么我应该看到每次运行时相同的测试都会失败,无论如何。这 27 个测试应该在每次运行时都失败,而不仅仅是在我运行其他所有测试时。

“你不能在类之间有唯一的主键,并且事情没有被清理。” 看上面。即使我在我的类中重复了主键(我没有,因为我亲自对各个类中测试数据的主键的唯一性进行了三次检查),因为这些测试不是在单独的线程上同时运行的(已通过记录 ThreadId 进行了验证),任何给定测试的清理代码都将清除重复的数据,无论如何。

“你不能使用连接池。” 不,其实我是。而且我已经使用 SQL Profiler 验证了请求肯定是合并的。此外,因为这些测试不是并行运行的,所以只有一个连接线程。

“你不应该使用连接池。” 嗯,是的,我应该,因为底层代码库支持各种 Web 项目,但为了论证,我尝试在禁用连接池的情况下运行所有​​测试(在连接字符串中使用 Pooling=false),我得到了完全相同的结果。行为上没有任何变化。

“你当地的环境肯定有问题。” 我在其他同事的开发箱(偶然使用 SQL 2012)上运行这些测试也得到了相同的结果。这不是我的环境所独有的,甚至不是我的 SQL Server 版本所独有的。

“您应该尝试从命令行运行 mstest。” 已经这样做了。结果相同。

如果有人遇到过这样的事情,请告诉我。我知道我一定缺少一些简单的东西,因为这类问题通常就是这种情况,但我已经尽可能多地尝试解决这个问题。

4

2 回答 2

2

以下假设基于您的数据库处于完全恢复模式并且您在测试期间不执行任何恢复或其他诡计(例如分离/重新连接数据库等)的假设。

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

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

  2. 创建 Sql Profiler 跟踪对于事件,选择 RPC 启动/完成、sql 批处理启动/完成、Sql 语句启动/完成、SP 语句启动/完成、TM:* 完成、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 有一个改进的数据库恢复向导,使时间点恢复变得非常容易。 祝你好运!

于 2013-04-19T06:25:22.287 回答
0

不知道为什么你的失败,但我有类似的东西,现在我transactionscope在设置中放了一个这样的:

public void SetUp()
{
 _transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew);
}

并将其丢弃在拆解处。这摆脱了我的数据库问题,让我不必编写手动清理代码。

于 2013-04-11T20:47:30.617 回答