单元测试中的异常处理

最近项目不是十分多,所以大部分的学习来自于书本还有同行的博客,淘宝的QA Team博客就是一个很好博客,里面N多淘宝的工程师写,而且更新的非常频繁,质量也很好。翠翠同学以后就要加入淘宝了,以后就能看到他写的博客了。

早上来看到一篇文章,大概讲的是在单元测试中容易用到了Try…Catch语句而容易出现的一个错误,这里想说一下我对单元测试中异常处理的。记得一个牛人曾经说过(实在想不起来谁也搜不到),大概的意思就是“处理一个问题的最好的办法就是不去处理它”。我不知道当时他讲这句话的具体场景是什么,不过我觉得这句话用在单元测试的异常处理中还是比较合适的。

首先来看看两条关于异常处理的原则

  • 如果无法处理某个异常,那么就不要捕获它
  • 如果捕获到一个异常,那么不要胡乱处理它

单元测试的代码和开发的生产代码,虽然同是程序代码,但是他们存在的意义是不一样的。前者是验证程序的正确性,属于为程序服务的;后者是实现某种功能,属于为客户服务的。然后在看上面的两条异常处理的原则。
Continue reading “单元测试中的异常处理”

测试工程师可以修复BUG吗?

昨天Check in了几行代码,部署到了生产环境,一切正常,这个是我第一次把代码提交到生产环境。今天早上看到一篇文章“Who fixes the bugs?”。这篇文章是讲述了一个测试工程师是否应该去深入到代码,然后自己把发现的BUG修复,这样的一个事情。很有趣,我刚做完这样的事情,就看到一篇文章讲述同样的问题。 读完那篇文章以后会发现,软件测试真的是一件跟上下文(Context)联系的特别紧密的事情。所以在做某一件事,或者讨论某一件事情的时候,最好先把上下文先弄清楚,然后再下判断。下面阐述一下文章的观点。

  • 作为一个团队里面的成员,每个人都有义务和责任去提高产品的质量。
  • 测试工程师来修复BUG这样的行为虽然不会京城在传统的软件行业里面发生,但是也不能以这个为理由去打击它。
  • 你不必要去创造一个敏捷的过程来规定测试工程师可以修复BUG。

上面这三点非常模棱两可的观点,在我看来就是作者让读者知道,我们在充分了解上下文的前提下来进行判断。对于我现在所处的公司的环境下,昨天我做的事情是没啥问题的。

  1. 公司没有严格的流程或者指引,规定了Tester不能往生产的代码库中check-in代码
  2. 现在公司的Developer不是很多,有时候能帮的地方尽量帮助,自己也能学到东西
  3. 对于我测试过的代码,熟悉起来还是会比较快
  4. 我也需要对代码的质量负责

那么这样做会有什么问题呢?

  1. 变相地成了自己测试自己的代码,不推荐
  2. 对于测试小组来说,比较难留住人才(那篇文章的观点,也是一个现实)

在多数情况下,测试工程师的工作就是不断地折磨那个被测软件,不断地向被测系统提问题;然后等待被测系统的答案,从这些答案里面获得尽可能多的信息,软件测试在开发生命周期活动里面扮演一个服务者的作用,给开发经理,开发工程师,各个stakeholder们提供信息,所以软件工程师甚少去修BUG。而且在大多数非常正规的企业中,测试工程师设置没机会看到代码,所以也没有办法能修复BUG。软件工程师可以修复BUG吗?可以,不过不常见。

利用序列化和反序列化实现深拷贝

假如说有一个简单的类,只有2个属性,那么可以用比较简单的方法实现深拷贝。

[Serializable]
public class Person
{
    public int Height { get; set; }
    public string FirstName { get; set; }
}

class Program {
    static void Main(string[] args) {
        //实例化一个对象
        Person PersonOne = new Person();
        PersonOne.FirstName = "John";
        PersonOne.Height = 1;
        //深拷贝
        Person DeepCopyPerson = new Person();
        DeepCopyPerson.FirstName = PersonOne.FirstName;
        DeepCopyPerson.Height = PersonOne.Height;
    }
}

Continue reading “利用序列化和反序列化实现深拷贝”

测试代码重构实例

Martin Fowler的《重构》这本书基本上每个程序员都会看,对于做单元测试的测试工程师来说,测试的代码本身也是程序,也需要重构。最近在看《XUnit Test Patterns》,把以前的做的东西重新梳理了一下,并且落实到新的项目中。

首先来看看一个最原始的单元测试代码:

[TestMethod]
public void GetPaymentAccountByOwnerID()
{
	int ownerId = 1300100000;
	//Delete the account
	TestHelper.DeletePaymentAccountByOwnerId(ownerId);

	//Here should be create an account
	PaymentAccount paymentAccount = PaymentGateway.PaymentProvider.GetPaymentAccountByOwnerID(ownerId, AccountOwnerType.NormalUser);

	//Verify the payment account instance
	Assert.IsTrue(paymentAccount.AccountID > 0);
	Assert.AreEqual(DateTime.Now.DayOfYear, paymentAccount.CreateTime.DayOfYear);
	Assert.AreEqual(DateTime.Now.DayOfYear, paymentAccount.UpdateTime.DayOfYear);
	Assert.AreEqual(0, paymentAccount.Balance, 0.0001);
	Assert.AreEqual(0, paymentAccount.AvailableBalance, 0.0001);
	Assert.AreEqual(0, paymentAccount.FreezeAccount, 0.0001);
}

以上是个很简单的单元测试代码,应用了AAA原则,首先做好准备(删掉原有的数据),然后执行测试,最后再验证返回结果。看起来很好也很清晰。首先发现一个问题,就是那一堆Assert让人觉得迷惑,究竟想干啥?其实那6句Assert都是验证程序返回的PaymentAccount对象是是否符合设计。我把这些Assert语句提取出来,作为一个方法,让测试代码更加容易让人明白。也就有了版本2。
Continue reading “测试代码重构实例”

避免用户用一个邮箱在网站上注册多个帐号(马甲)

SNS的风已经刮过了,国内不少SNS网站正在刮第二阵风:游戏!像聚友网,开心网,山寨开心网,校内等社交网站都添加了大量的游戏,在这些游戏中都有一个共同之处–邀请朋友注册送积分(金币,道具等等……)。很多人就会多申请几个邮箱,然后再多注册几个马甲来获利。

我比较懒,一直想着有什么样的办法可以不申请更多的邮箱,而又能多注册几个马甲。可能大家都比较熟悉Google的Gmail邮箱,这个邮箱有一个特点就是它那神奇的“+”加号。例如现在我有这样一个邮箱abc@gmail.com那么我只需要在@符号和abc中间添加一个“+”,还有若干文字,就能“生成”一个新的邮箱,无需申请。例如abc+haha@gmail.com, abc+hi@gmail.com,这2个邮箱的邮件都发送到abc@gmail.com中,这就方便了我们注册马甲啦!
Continue reading “避免用户用一个邮箱在网站上注册多个帐号(马甲)”

单元测试中三种准备Test Fixture的方法比较

首先说一下Test Fixture,我不知道怎么样翻译这个Test Fixture,没能搜到一个翻译的比较合适的。最让我气愤的是某人翻译的一本书中,直接把Test Fixture翻译成为测试夹具,这明显就是什么词霸词典硬翻译出来的,我强烈鄙视这样不负责任的翻译行为。

The test fixture is everything we need to have in place to exercise the SUT

我觉得这是一个对Test Fixture的一个很清晰明了的定义,就是运行被测软件所需要的一切东西,这个“东西”不单只是数据,同时还包括对被测软件的准备,例如实例化某个被测方法所在的类,准备数据库的ConnectionString等。通常来说,有三种方法来准备Test Fixture。

1. 内联方式:这种方式就是直接在测试方法中编写准备Test Fixture的代码。用这种方法的缺点是很容易造成代码的重复,出现很多复制粘贴的代码。同时,如果这个SETUP的过程比较复杂,也会降低测试代码的可读性,可维护性。另外的一个问题就是,这种方法很容易会带来测试数据Hard code的隐患。既然有那么多缺点,这种方法还有什么生命力呢?首先,可能对于初学者来说,这种方法是最简单的;其次,在一些只需要准备简单的Test Fixture的场合中,这种方法还是给编写测试的人提供了便利。
Continue reading “单元测试中三种准备Test Fixture的方法比较”

控制反转有助于提高程序的可测试性

控制反转,英文是Inversion of Control,简称IoC。这个概念在JAVA的Spring框架中比较常见,在.NET的开发中,Ioc或者其的变种依赖注入DI(Dependency Injection)是不太常见的。一般来说,类A要使用类B,类A会在其内部对类B进行控制,比较典型的做法就是在类A中创建一个类B的实例;而控制反转,就是把一个已经创建好的类B实例交给类A去控制。
例子:
Continue reading “控制反转有助于提高程序的可测试性”

.NET平台上的Memcached客户端介绍

早上接到一个任务,需要对Linux服务器的Memcached的update操作进行性能测试,我发现我是一个典型的“手里拿着锤子,就把所有问题都当成钉子”的人。我第一个念头就是,上Memcached的官网找.NET的客户端。最后在Codeplex上找到了一个叫Memcached Providers的客户端程序,很小,218K,里面就3个DLL,一个是Memcached Providers本身的DLL,还有一个是Enyim.Caching,Enyim.Caching也是一个.NET平台上的Memcached客户端,最后就是著名的log4net。

Memcached Providers的配置很方便,首先就是在.NET项目中引用上述提到的3个DLL文件,然后就需要修改项目的配置文件,如果是桌面程序,就修改APP.CONFIG,如果是WEB程序,就修改WEB.CONFIG。
Continue reading “.NET平台上的Memcached客户端介绍”

自动化单元测试要点

用单元测试的框架MSTEST,做单元测试,集成测试快1年了,总结一下工作中学到都东西。

单元测试,集成测试有什么用?

1. 改进产品质量

软件测试,很多时候围绕着两个问题:

Verification和Validation,常说的双V。前面的Verification就是Is the software built correctly?。后面的Validation就是Have we built the right software?

单元测试,更多的是Verification。所以有时候经过我单元测试和集成测试以后的功能模块,在交给其他同事做功能测试的时候,依然会有一些BUG,这时候开发可能会埋怨我测试得不够完全,诸如此类。但是其实很多时候,我的关注点是单个方法的功能、行为,没有站到更高的位置来测试这个模块。对于这样的问题,开发和测试应该互相体谅,我本人也会提高自身的水平。希望在单元测试和集成测试中也加入更多关于Validation的思考。
Continue reading “自动化单元测试要点”

单元测试的坏味道

Martin Fowler有一本很出名的书《重构》,里面有个很出名的概念,Code Smell。前阵子我也刚发现一本很好的书,《XUnit Test Patterns》。这本书主要讲的是如何重构测试代码,这里的测试代码指的就是自动化测试的代码,再进一步细化就是单元测试为主的自动化测试代码的重构。由于此书已经让清华大学翻译烂了……所以建议大家下载英文版。

所谓的测试的坏味道,有三种:

  1. 项目(Project)
  2. 行为(Behavior)
  3. 代码(Code)

按照《重构》书中提出的坏味道的概念来看,如果说是有坏味道(Smell),那么大多数都是指代码级别的。什么的坏味道?就是程序有可能会有大问题的一种征兆(symptom)。个人理解就是不能被直接观察到的现象,可以称之为坏味道。

对于自动化测试代码来说,如果有了项目级的坏味道,通常变现就是生产环境出现了BUG。如果出现这样的情况,那么自动化测试代码就不是有味道那么简单了,而是有问题了,自动化测试的安全网没有能够抓住这个BUG。
Continue reading “单元测试的坏味道”