**序言:**我设计了一个强接口和完全可模拟的数据层类,当多个调用应该包含在单个事务中时,它期望业务层创建TransactionScope
。
**问题:**我想对业务层使用TransactionScope
对象的情况进行单元测试。
不幸的是,使用TransactionScope
的标准模式如下:
using(var scope = new TransactionScope())
{
// transactional methods
datalayer.InsertFoo();
datalayer.InsertBar();
scope.Complete();
}
虽然就程序员的可用性而言,这确实是一个很棒的模式,但它所做的测试似乎……对我来说不可能。我无法检测到一个 transient 对象已经被示例化,更不用说模拟它来确定对它调用了一个方法。然而,我的目标覆盖面意味着我必须。
**问题:**如何构建单元测试,以确保TransactionScope
根据标准模式正确使用?
**最后的想法:**我考虑过一个解决方案,它肯定能提供我需要的覆盖率,但我拒绝了它,因为它过于复杂,不符合标准的TransactionScope
模式。它涉及到在我的数据层对象上添加一个CreateTransactionScope
方法,该方法返回TransactionScope
的示例。但是由于TransactionScope包含构造函数逻辑和非虚方法,因此即使不是不可能也很难模拟,CreateTransactionScope
将返回DataLayerTransactionScope
的示例,这将是TransactionScope
的可模拟外观。
虽然这可能会做的工作,它的复杂性,我宁愿使用标准模式。有更好的办法吗?
6条答案
按热度按时间ut6juiuv1#
我现在正面临着同样的问题,对我来说,似乎有两个解决方案:
1.不要解决问题。
1.为现有的类创建抽象,这些类遵循相同的模式,但是是可模拟/可存根的。
**编辑:**我已经为此创建了一个CodePlex-project:http://legendtransactions.codeplex.com/
我倾向于创建一组用于处理事务的接口和一个委托给System的默认实现。
这看起来是一个很大的工作,但另一方面,它是可重用的,它使它非常容易测试。
请注意,这并不是接口的完整定义,足以给予您了解全局。
**编辑:**我只是做了一些快速和肮脏的实现作为概念证明,我认为这是我将采取的方向,这是我到目前为止提出的。我在想,也许我应该为此创建一个CodePlex项目,这样这个问题就可以一劳永逸地解决了。这不是我第一次遇到这种事了。
下面是一个依赖于ITransactionManager来处理其事务性工作的类的示例:
3pvhb19x2#
我发现了一个很好的方法来测试这一点,使用Moq和FluentAssertions。假设你的测试单元看起来像这样:
你的测试看起来像这样(假设MS测试):
这对我的目的很有效。
7hiiyaii3#
不管这个测试是好是坏...
非常肮脏的黑客是检查交易.当前不是空.
这不是一个100%的测试,因为有人可能使用TransactionScope以外的其他东西来实现这一点,但它应该防止明显的“懒得有事务”部分。
另一种选择是故意尝试创建一个新的TransactionScope,该TransactionScope的隔离级别与任何将/应该使用的和
TransactionScopeOption.Required
不兼容。如果此操作成功而不是抛出ArgumentException,则不存在事务。这需要您知道某个特定的IsolationLevel未使用(可能会选择Chaos之类的)这两个选项都不是特别令人满意的,后者非常脆弱,并且受制于TransactionScope的语义保持不变。我会测试前者而不是后者,因为它更健壮(并且易于阅读/调试)。
t9eec4r04#
我是一名Java开发人员,所以我不确定C#的细节,但在我看来,你需要两个单元测试。
第一个应该是成功的“蓝天”测试。您的单元测试应该确保所有ACID记录在事务提交后出现在数据库中。
第二个应该是“wonky”版本,它执行InsertFoo操作,然后在尝试InsertBar之前抛出异常。一个成功的测试将显示异常已经被抛出,并且Foo和Bar对象都没有被提交到数据库。
如果这两个都通过了,我会说您的TransactionScope正在正常工作。
byqmnocz5#
在我自己思考了同样的问题之后,我得出了以下解决方案。
将图案更改为:
当您需要测试代码时,您可以继承被测试的类,扩展函数,这样您就可以检测是否调用了它。
然后在类的可测试版本上执行与TransactionScope相关的测试。
fhity93d6#
创建一个wrapper:
然后用工厂方法进行DI