Fork me on GitHub
使用事务自动回滚来实现单元测试

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

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

我们看AutorollbackAttribute的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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的单元测试框架里了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/// <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”的文档

原文地址:http://blog.moozi.net/archives/use-the-transaction-rollback-to-unit-test.html 
  

02.C# 技术

C# 技术
摘要: 我们没有使用TDD,所以单元测试最麻烦的就是准备测试的基础数据。我们现在是使用内存仓储来做单元测试,要为每个仓储都构造基础数据,非常麻烦。 前几天看xunit的源码,看到AutoRollbackAttribute这个特性,异常的兴奋 ^_^。怎么就忘了用事务的自动回滚呢? AutorollbackAttribute使用了.Net Framework自带的TransactionScope。TransactionScope在.NET 2.0中就已经有了,可用于分布式事务。用这种方法来做数据的自动回滚也有一些不足: 1、数据库要支持事务。 2、内部数据库操作的逻辑里没有事务的实现。 很庆幸的是我们的项目正好都满足上面的2点,唯一不足的就是mongodb不支持事务。所以就需要混合仓储实现了,事务数据库使用真实的仓储,mongodb使用内存仓储。阅读全文
posted @ 2012-03-14 21:43 木子博客 阅读(481) | 评论 (3) 编辑
 
摘要: WEB项目中除了单元测试,还经常需要多线程测试一个方法是否存在并发问题,或者是否有性能问题。每次都要写测试代码总是一件很累的事情。于是写了这一个多线程测试的类库,用来进行快速的多线程并发测试。阅读全文
posted @ 2011-04-16 20:21 木子博客 阅读(2390) | 评论 (13) 编辑
 
摘要: 上个月公司项目需要,在腾讯社区开放平台部署了基于mono的腾讯校友应用“公务员考试”。经过一周的观察,发现问题非常大。 2月18日到2月24日的平均值在70.91%。而2月23日-3月1日平均值在76.61%。占用的CPU非常高。在访问用户数不高的情况下,单是MONO的守护进程就占用了75.0%的CPU。 生产环境上是用Apache+mod_mono来做WEB站的,总的来看,对于WEB负载,MONO的情况并不理想。阅读全文
posted @ 2011-03-02 21:07 木子博客 阅读(3085) | 评论 (16) 编辑
 
摘要: 继上篇文章《Mono的兼容性问题》之后,现在总结一下我在suse 10中部署mono环境遇到的一些问题及其处理办法。 腾讯社区开放平台提供的体验区的服务器环境是这样的:Linux version 2.6.16.60 (gcc version 4.1.2 20070115 (SUSE Linux)) 这个Linux内核的版本号应该是SLES(SUSE Linux Enterprise Server 10 SP2)的操作系统。mono原来的版本号是1.2.2,而我们需要2.8.2的mono。腾讯的服务器是没有配置dns解析功能的,所以不能直接用yast2来通过更新源来升级mono;对于可以用yast2安装的,可以参考我的《在openSUSE中部署基于apache的mono环境》。而用rpm手工安装,太多的程序依赖处理起来很麻烦,只好用通过编译mono源码的方式来安装了。阅读全文
posted @ 2011-01-26 20:06 木子博客 阅读(1770) | 评论 (11) 编辑
 
摘要: 最近公司的一个项目因为要接入腾讯开放平台,需要转移到mono环境中,在做mono兼容性调整的时候遇到了一些问题,记录下来跟大家分享一下。 借助Moma的分析结果可以指导我们如何快速的调整mono的兼容性,但是这个分析结果也不完全正确,一些实现方式MoMA提示不兼容,但在mono中是可以正常使用的。 总的说来,mono整体的兼容性是非常不错的,在linux中跑起来很流畅,mono 2.8.2已经不再是玩具了。阅读全文
posted @ 2011-01-24 00:28 木子博客 阅读(1870) | 评论 (11) 编辑
 
摘要: 腾讯社区开放平台是基于QQ空间、朋友社区(QQ校友)两大社交网络的开放平台。至今官方提供了一系列的开放接口(Open API)和开发工具包(SDK),开发者可以通过Open API获取登录用户信息、用户签名信息以及好友关系链信息等。已上线的应用还可以调用支付和监控接口,实现支付功能,查看应用实时运维数据。 腾讯官方给出了不少的SDK,点这里进入官方地址。遗憾的是没有开放C#的SDK。 我们DotNet开发者不能落后其它平台的开发者,这里开源我的腾讯社区开放平台SDK。阅读全文
posted @ 2010-11-05 20:41 木子博客 阅读(9299) | 评论 (21) 编辑
 
摘要: HashTable数据结构存在问题:空间利用率偏低、受填充因子影响大、扩容时所有的数据需要重新进行散列计算。虽然Hash具有O(1)的数据检索效率,但它空间开销却通常很大,是以空间换取时间。所以Hashtable适用于读取操作频繁,写入操作很少的操作类型。 Dictionary 也是用的Hash算法,通过数组实现多条链式结构。不过它是采用分离链接散列法。采用分离链接散列法不受到装填因子的影响,扩容时原有数据不需要重新进行散列计算。 SortedDictionary是按照K有序排列的(K, V)数据结构,以红黑树作为内部数据结构对K进行排列保存– TreeSet,红黑树是一棵二叉搜索树,每个结点具有黑色或者红色的属性。它比普通的二叉搜索树拥有更好的平衡性。阅读全文
posted @ 2010-05-23 12:06 木子博客 阅读(3220) | 评论 (5) 编辑
 
摘要: 每个项目都在无休无止的用到跨站脚本过滤,大家都是自己写,于是各种各样的正则表达式层出不尽。推荐的这个微软的反跨站脚本库,个人感觉是非常棒的,也不再需要为自己的代码是否过滤完全而绞尽脑汁了。阅读全文
posted @ 2010-03-04 00:23 木子博客 阅读(872) | 评论 (6) 编辑
 
摘要: NUnit的使用是非常简单的,但是它在项目中使用时,有许多最佳实践。这章我们把上一章没有讲到的NUnit的一些重要属性及其用法介绍给大家。 要想熟练地使用NUnit还是要在实践中使用和体会,单纯地学习知识点是没有用的。接下来的章节会从实例出发学习NUnit。阅读全文
posted @ 2009-03-21 20:54 木子博客 阅读(2337) | 评论 (9) 编辑
 
摘要: Petshorp这么做是为了实现商业逻辑层能跨数据库复用(反射工厂模式),利用ADO.net的框架很容易解决数据库访问的统一外观问题,但是具体每个业务查询的Sql语句仍然带有数据库特性。所以Petshorp有一个DAL(Data Access Layer)数据访问层专门写数据库查询语句并做具体和数据库特性有关的数据处理供商业逻辑层使用,既然商业逻辑层不关心这些Sql实现,当然要定义统一的接口IDAL。阅读全文
posted @ 2008-04-17 09:45 木子博客 阅读(2841) | 评论 (7) 编辑
 
摘要: PetShop多层精典阅读全文
posted @ 2008-04-15 16:11 木子博客 阅读(512) | 评论 (0) 编辑
 
摘要: 支付宝接口源代码(刚完成,应该是目前最好的了) 支付宝的接口调用很不方便,刚做好一个封装,实现了虚拟交易和实物交易。 解决方案中有三个项目以及NDoc生成的文档,简单的序列图:CommonAliPay,封装的支付宝接口阅读全文
posted @ 2008-03-19 19:02 木子博客 阅读(1192) | 评论 (0) 编辑
 
摘要: abstract 
abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。 
virtual 
virtual 关键字用于修改方法或属性的声明,在这种情况下,方法或属性被称作虚拟成员。虚拟成员的实现可由派生类中的重写成员更改。阅读全文
posted @ 2008-03-19 18:50 木子博客 阅读(267) | 评论 (2) 编辑
 
摘要: 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,我将通过两个范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来、.Net Framework中的委托和事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。阅读全文
posted @ 2008-03-19 18:35 木子博客 阅读(167) | 评论 (0) 编辑
 
摘要: C#中国身份证验证,包括省份验证和校验码验证,符合GB11643-1999标准... 忽略末尾X的大小写。用560个身份证号测试,只有2个未通过,应该是身份证号登记错误的。验证过程应该是没有问题了。阅读全文
posted @ 2008-03-19 13:38 木子博客 阅读(2637) | 评论 (2) 编辑
 
摘要: 本文实现一个简单的多线程客户/服务器即时通讯应用程序,它将使用TCP连接。允许客户之间及客户和服务器之间,个人消息与会议通信。最终目标是实现象雅虎/msn这样的即时通讯工具。阅读全文
posted @ 2008-03-19 13:08 木子博客 阅读(1141) | 评论 (0) 编辑
posted on 2012-03-16 09:24  HackerVirus  阅读(1803)  评论(0编辑  收藏  举报