我们没有使用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使用内存仓储。