使用事务自动回滚来实现单元测试

Angie ·
更新时间:2024-11-10
· 773 次阅读

  我们没有使用TDD,所以单元测试麻烦的是准备测试的基础数据。我们现在是使用内存仓储来做单元测试,要为每个仓储都构造基础数据,非常麻烦。

  前几天看xunit的源码,看到AutoRollbackAttribute这个特性,异常的兴奋 ^_^。怎么忘了用事务的自动回滚呢?

  我们看AutorollbackAttribute的具体实现:

public class AutoRollbackAttribute : BeforeAfterTestAttribute {     IsolationLevel isolationLevel = IsolationLevel.Unspecified;     TransactionScope scope;     TransactionScopeOption scopeOption = TransactionScopeOption.Required;     long timeoutInMS = -1;

     /// <summary>     /// Gets or sets the isolation level of the transaction.     /// Default value is <see cref="IsolationLevel"/>.Unspecified.     /// </summary>     public IsolationLevel IsolationLevel     {         get { return isolationLevel; }         set { isolationLevel = value; }     }

     /// <summary>     /// Gets or sets the scope option for the transaction.     /// Default value is <see cref="TransactionScopeOption"/>.Required.     /// </summary>     public TransactionScopeOption ScopeOption     {         get { return scopeOption; }         set { scopeOption = value; }     }

     /// <summary>     /// Gets or sets the timeout of the transaction, in milliseconds.     /// By default, the transaction will not timeout.     /// </summary>     public long TimeoutInMS     {         get { return timeoutInMS; }         set  { timeoutInMS = value; }     }

     /// <summary>     /// Rolls back the transaction.     /// </summary>     public override void After(MethodInfo methodUnderTest)     {         scope.Dispose();     }

     /// <summary>     /// Creates the transaction.     /// </summary>     public override void Before(MethodInfo methodUnderTest)     {         TransactionOptions options = new TransactionOptions();         options.IsolationLevel = isolationLevel;         if (timeoutInMS > 0)            options.Timeout = new TimeSpan(timeoutInMS * 10);         scope = new TransactionScope(scopeOption, options);     } }

  这里使用了.Net Framework自带的TransactionScope。TransactionScope在.NET 2.0中已经有了,可用于分布式事务。用这种方法来做数据的自动回滚也有一些不足:

  1、数据库要支持事务。

  2、内部数据库操作的逻辑里没有事务的实现。

  很庆幸的是我们的项目正好都满足上面的2点,不足的是mongodb不支持事务。所以需要混合仓储实现了,事务数据库使用真实的仓储,mongodb使用内存仓储。

  项目中是用VS自带的单元测试框架,也不想因为这一个特性而改用xunit,那只能动手把这个迁移到VS的单元测试框架里了。

/// <summary> /// 单元测试基类 /// </summary> [TestClass] public class BaseUnitTest {     IsolationLevel _isolationLevel = IsolationLevel.Unspecified;     TransactionScopeOption _scopeOption = TransactionScopeOption.Required;     TransactionScope _transactionScope;     bool _openAutoRollback = true;

     /// <summary>     /// 构造函数     /// </summary>     /// <param name="autoRollback">是否开启自动回滚,默认开启</param>     public BaseUnitTest(bool autoRollback = true)     {         _openAutoRollback = autoRollback;     }

     /// <summary>     /// 自动回滚事务初始化     /// </summary>     [TestInitialize]     public void AutoRollbackBefore()     {         if (_openAutoRollback)         {             var options = new TransactionOptions();             options.IsolationLevel = _isolationLevel;             options.Timeout = new TimeSpan(0, 1, 0);             _transactionScope = new TransactionScope(_scopeOption, options);         }     }

     /// <summary>     /// 自动回滚事务回滚并释放对象     /// </summary>     [TestCleanup]     public void AutoRollbackAfter()     {         if (_openAutoRollback)         {             if (_transactionScope == null)                 throw new InvalidOperationException("未初始化TransactionScope");             //回滚事务             _transactionScope.Dispose();             //释放事务对象             _transactionScope = null;             //移除所有的缓存             RemoveHttpRuntimeCache();         }     }

     /// <summary>     /// 移除所有的HttpRuntime缓存     /// </summary>     [DebuggerStepThrough]     private void RemoveHttpRuntimeCache()    {         var cache = HttpRuntime.Cache.GetEnumerator();         var keys = new List<string>();         while (cache.MoveNext())         {             keys.Add(cache.Key.ToString());         }         foreach (var key in keys)         {             HttpRuntime.Cache.Remove(key);         }     }

     /// <summary>     /// 设置不自动回滚事务     /// </summary>     protected void SetAutoRollbackIsUnavailabled()     {         _openAutoRollback = false;     } }

  上面的RemoveHttpRuntimeCache是因为我们在项目中有使用HttpRuntime缓存,关系数据库中的数据回滚后会导致缓存和数据库不一致,所以一但有开启事务的自动回滚,也要相应的清空内存缓存。

  方法很简单,跟大家分享一下,和TransactionScope相关的知识,不清楚的同学可以看下MSDN关于“TransactionScope”的文档。



单元 单元测试 回滚 自动 测试 事务

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章