2010-10-07 53 views
3

我正在写一些针对数据库的单元测试,我们正在使用事务来确保我们的测试数据在最后被移除。TransactionScope导致阻塞?

我遇到了一个问题,我正在测试的方法是使用它们自己的TransactionScope对象,并且它在访问数据库时似乎被阻塞。

这是我测试的基类中:

BaseScope = new CommittableTransaction(new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUnCommitted, Timeout = new System.TimeSpan(0, 5, 0) }); 

,然后我测试的方法中,它的作用:

using (TransactionScope scope = new TransactionScope()) 

的第一次,第二次范围内的代码的触及数据库,它挂起。我有办法解决这个问题吗?

回答

6

如果您使用的是数据库,那么您并未进行单元测试,而您遇到的问题是真正的单元测试使用Mocks和Stub的原因之一。

现在你正在做的测试是非常有价值的,在某些情况下,我实际上会做它们而不是单元测试。我将这个早期整合测试(EIT)标出来。关键在于我们在处理真实的事情而不是单元测试模拟时发现了一类全新的错误。这里的关键是真正的事情。只要你用人为的交易范围假装环境,你就会失去EIT的许多好处,因为你不会捕捉到细微的交互错误,或者(如你的情况)引入人为的问题。

我会找到一种方法来快速填充数据库并提供足够的测试数据,并将其恢复到测试以外的状态。 “重置为已知状态”脚本对于这些测试非常有用。

+0

我认为这是我最终选择的选项。我编写了一些代码来创建一个在测试开始时运行的新/空数据库,最后数据库被删除。 – Jonas 2010-10-08 00:15:38

4

当您嵌套TransactionScope实例时,您最终可以得到分布式事务,而不是简单的本地事务。此行为在使用数据库之间有所不同。例如,除非实际涉及多个数据库,否则SQLServer 2008不会升级到DTX。另一方面,Oracle将始终升级为分布式事务,因为它不能支持单个本地事务的共享连接。

根据您正在使用哪个数据库和什么TransactionScopeOption,最终可能会导致死锁。发生这种情况是因为DTX通常需要表锁来确保它们可以以原子方式提交。例如,在Oracle中,如果您在完成DTX并发生故障或失去连接之前启动DTX,则最终会出现“疑难分布式事务”。这个“不确定”事务可以锁定一个或多个表,阻止其他会话修改它们,直到DBA对未决的事务ID执行命令ROLLBACK FORCE。有些数据库(如SQLServer)试图检测这种死锁并终止其中一个违规交易......但这是保证发生的。

我建议两个选项之一为您提供:

  1. 决定是否真的需要编写访问数据库的测试。通常,您可以使用模拟或存根来避免编写测试来改变数据库,然后回滚数据库。避免这些问题是有道理的,因为它既加速了你的测试,也消除了潜在的依赖。但是,有时你不能这样做。
  2. 如果你真的需要测试你的逻辑对数据库,考虑修改你的代码,使所有方法都使用相同的数据库连接来执行他们的SQL。这将消除创建分布式事务,并希望解决您的问题。

您可能还需要寻找到你的数据库的未决事务视图(在Oracle中,它被称为PENDING_TRANS$ ......在SQLServer的还有的XACT_STATE()功能)。

3

你必须为了解除封锁的测试方法,我假设提交的基本交易是不是你想要的那种行为。你需要让你的测试方法的事务加入你的基类中创建的外部“环境”(伞/父/基/外)事务,而不是尝试创建它自己的事务。

从MSDN CommittableTransactionClass问(重点煤矿):

建议您使用 TransactionScope类创建 隐含的交易,从而使 环境事务上下文 自动为您进行管理。 您 还应该使用的TransactionScope 和DependentTransaction类为需要跨多个 函数调用或多个线程调用 使用 同一事务 应用。有关此 模型的更多信息,请参阅Implementing An Implicit Transaction Using Transaction Scope话题。

创建CommittableTransaction不 不会自动设置环境 交易,这是 你的代码在执行交易。你可以得到或 致电 的 全球交易对象的静态Current属性设置环境事务。有关环境交易的更多 信息,请参阅实施隐含 交易使用交易范围 主题的“使用TransactionScopeOption管理交易流程 ”部分 。如果环境事务 没有设置任何操作上的资源 经理是不是 交易的一部分。你需要明确 设置和复位的环境事务, 确保资源管理者 权交易 环境下运行。

直到CommittableTransaction一直致力于中,所有参与交易的资源仍处于锁定状态。

正如djna指出的那样,使用事务来回滚测试期间所做的更改非常具有侵犯性。你测试应该是一个很好的公民并且撤消并且改变它在一个finally子句中使它自己到数据库中,以便在它之后可能运行的其他测试从不受影响。如果你有很多已经没有很好表现的测试,那么你现在可能不会走这条路线。在这种情况下,请将您的基础更改为使用范围设置为RequiresNew的隐式事务,并在您的测试方法中使用Required

+0

我只想指出,我对这件事的赞成是完全不相关的,似乎是一个“复仇”投票,因为我在同一时间收到了5个赞同人的旧字串:/ – 2010-10-31 15:55:23

+0

谢谢达米安:)我很漂亮当然,我的答案是一个很好的答案。 – 2010-12-04 16:58:26

相关问题