1.3 互联网特色的单元测试

在接下来的一节里,让我们把关注的目标缩小,更集中一点,来看看什么是具有互联网特色的单元测试。

1.3.1 网站为什么要做单元测试

首先需要搞清楚的问题是,网站为什么要做单元测试?或者说,网站单元测试能够给我们带来哪些好处?

前文已经说过,互联网应用软件,或者说网站应用的最基本要求是“短、平、快”,也可以称之为“高频率、小改动”,为了满足这个要求,需要在保证软件质量的前提下,尽可能地缩短软件开发和测试的时间。那么软件开发和测试的时间如何才能缩短?最容易被减少的,就是测试反复的次数,以及修复测试发现的问题所花费的时间。

一般的软件测试理论都认为,软件测试在整个软件生命周期中所占的比例在50%以上,具体再分析一下,不难发现其实进行一遍软件测试,找出软件缺陷的时间并不长,大量的时间被花费在以下这些地方:

1.开发工程师定位并修复QA工程师系统测试所发现的缺陷。

2.QA工程师验证开发工程师修复的缺陷。

3.已修复的缺陷经过验证后仍然没有解决。

4.已修复的缺陷经过验证后确认已经解决了,但是修改的代码引起了其他新的缺陷。

5.某个缺陷阻塞了功能,导致后面有大量缺陷还未被发现,当该缺陷被解决后,暴露出了更多的缺陷。

6.已经进入回归测试阶段,并且回归测试快要完成时,却发现严重的缺陷,这可能是由于开发工程师修改代码时不小心犯了错误,结果这次回归测试宣告失败,QA工程师必须等开发工程师解决严重缺陷后再次进行回归测试。

7.对于性能测试所发现的软件性能问题,往往很难定位,通常都需要反复进行性能测试,并评审大量的代码。

很好,现在已经很清楚地知道了时间究竟都花费在哪里了,那么下一步就是想办法来缩短这些时间。

仔细研究后可以发现,造成上述7点时间花费的原因可以归结为:

1.代码质量不高,存在很多缺陷。

2.系统测试发现的缺陷比较难以定位。

3.为了修复缺陷而修改代码时,很可能会不小心犯错,但是又不能及时发现这些新错误。

4.性能问题很难定位,性能优化的时间很难控制。

好了,其实到这里,解决问题的答案已经出来了,单元测试能解决全部的4个问题:

1.经过单元测试的代码,其质量能够得到保证(当然这取决于单元测试是否是有效的),大部分缺陷能够在单元测试时被发现并解决。

2.单元测试发现的缺陷很容易定位,经过有效的单元测试并修复测试所发现的缺陷后,剩下的留到系统测试阶段才被发现的缺陷已经没有几个了,并且缺陷之间的相互干扰也会变小,更容易定位。

3.即使在修改代码时不小心犯了错,通过单元测试,立刻就能发现所犯的错误,避免等到QA工程师在回归测试时才发现严重的错误。

4.性能问题同样是越早发现越容易定位,在单元测试的时候就进行性能测试,可以发现相当一部分性能问题,从而在早期就解决它们,避免了后期的定位困难,以及性能问题相互影响导致情况变得更加复杂。

另外,需要特别说明的是,在各个阶段的软件测试中,单元测试是能够达到自动化程度最高的一个,这是由各个阶段软件测试的特性所决定的。通常对于系统测试而言,自动化程度一般最多只能达到20%~30%,而单元测试用例,可以说就是完全自动执行的。这在回归测试以及代码重构后的测试时作用非常明显,高度的自动化单元测试意味着更高的测试效率、更短的测试周期、更高的可靠性。

1.3.2 网站应用架构与单元测试

互联网应用软件需要进行分层的测试,这对于单元测试来说同样如此。由于每一层具有不同的特性,因此有些层很容易进行单元测试,有些层进行单元测试则会很困难。

首先说明一下,这里讨论的层次结构指的是应用架构的层次结构,而不是网站体系结构的层次结构。这两者有什么区别呢?简单地说,应用架构是应用程序的逻辑层次结构,而网站体系结构则是整个网站的物理层次结构。举例说明一下,二层应用架构包含表示层和应用逻辑层这两层,而二层网站体系结构则包括客户端(包含所有用户界面和应用逻辑)和数据库服务器;同样,三层应用架构包含表示层、业务逻辑层和数据访问层,而三层网站体系结构则包括客户端(包括所有用户界面,一般是浏览器)、应用服务器(包括业务逻辑和数据访问)和数据库服务器。大致上,网站的应用架构可以分为三大类:二层结构、三层结构以及多层结构。

1.二层结构:二层结构是最原始、最简单的应用架构,它仅仅包含表示层和应用逻辑层,如图1.1所示。

图1.1 二层结构应用架构

在二层结构应用架构中,表示层只能访问应用逻辑层,应用逻辑层也只能被表示层访问;数据库只能被应用逻辑层访问,应用逻辑层通过在代码中直接写SQL语句的方式来访问数据库。在这种应用架构中,应用逻辑层其实做了两件事情,既对业务逻辑进行了处理,又访问了数据库,因此,应用逻辑层包含了业务逻辑和数据访问。该结构最大的优点在于结构简单,开发和运行的环境简单。但也正是这种结构,产生了其原生性的问题:

● SQL是结构化的语言,因此让习惯于面向对象语言的开发人员来说,编写SQL是一件低效而乏味的工作,开发人员难以专注于业务逻辑。

● 由于代码中含有大量硬编码的SQL,代码无法有效地复用。

● 业务逻辑与数据访问的代码混杂在一起,使得代码难以重构。

● 数据库表结构的变动可能会导致需要修改大量代码。

● 代码的可测试性比较低。

正因为二层结构具有这些原生性问题,仅仅依靠对二层结构进行细枝末节的修补和开发,无法很好地解决实际问题。而要真正解决这些问题,必须从根本上改变这种二层结构设计。于是,为了解决这些问题,三层结构的应用架构出现了。

2.三层结构:三层结构是一种成熟、简单并得到普遍应用的应用架构,它将应用程序结构划分为三层独立的包,包括表示层、业务逻辑层、数据访问层。其中将实现人机界面的所有表单和组件放在表示层,将所有业务规则和逻辑的实现封装在负责业务逻辑的组件中,将所有和数据库的交互封装在数据访问组件中。三层结构如图1.2所示。

图1.2 三层结构的应用架构

三层结构是目前流行的架构设计模式,它通过分解来管理问题的复杂性,同时还可以有效地重复使用业务逻辑,并保留与昂贵资源(如数据库)的重要连接。在三层结构中,包通常所需要处理的是一个具体的功能区域(业务逻辑),或者是一个具体的技术区域(技术逻辑)。业务逻辑主要考虑的是对系统业务功能的实现,而技术逻辑则是进一步考虑用户界面、数据库或通信机制等形成的技术方案。把技术逻辑和业务逻辑区分开来是极其重要的,这是为了当修改程序的某一部分时不会对另一部分产生影响,更加便于进行“复用”,同时易于应对来自业务逻辑的变更需求。

三层结构是一种严格分层方法,即数据访问层只能被业务逻辑层访问,业务逻辑层只能被表示层访问,用户通过表示层将请求传送给业务逻辑层,业务逻辑层完成相关业务规则和逻辑,并通过数据访问层访问数据库获得数据,然后按照相反的顺序依次返回将数据显示在表示层。由于三层结构将业务逻辑和数据访问分开,因此开发人员可以专注于业务逻辑,而不用关心如何去访问数据库,代码可以有效地复用,并且易于重构;通过数据访问层定义的数据访问对象(DAO),开发人员可以用自己习惯的面向对象的方式来访问数据库;将不同职能的代码划分为清晰的层次结构,使得代码的可测试性大大提高。三层结构的出现,解决了二层结构的原生性问题。

对三层结构进一步地划分,就产生了四层结构、五层结构等,这其中就包括了当前非常热门的SOA(面向服务的架构)。一般把这些应用架构统称为多层结构,或者称之为N层结构。

3.多层结构:先来看看四层结构,以微软的Duwamish 7.0(微软Visual Studio .NET附带的例子,用以说明.NET技术的特点)为例,它是一个典型的N层结构,其结构分为四个逻辑层,表示层、业务外观层、业务规则层和数据访问层,其结构如图1.3所示。

图1.3 Duwamish结构关系图

从图中可以清楚地看到,浏览器首先调用的是表示层Web,然后Web将请求发送给业务外观层,业务外观层对请求进行初步的处理,判断是否需要调用业务规则层,还是直接调用数据访问层获取数据,最后由数据访问层访问数据库,并按照来时的步骤返回结果到表示层,在浏览器上展示。

为什么这里有别于三层结构?这里的业务外观层、业务规则层与三层结构中的业务逻辑层又有什么联系呢?简单说明一下。在Web应用程序中,有部分操作只是简单地从数据库根据条件提取数据,不需要经过任何处理,而直接将数据显示到网页上,比如查询某类别的图书列表。而另外一些操作,比如计算订单中图书的总价并根据顾客的级别计算回扣等,这部分往往有许多不同的功能的类,操作起来也比较复杂。可以先想象一下,如果采用三层结构,这些商业逻辑一般是会放在业务逻辑层,那么对内部这些大量种类繁多、使用方法也各异的不同类的调用任务,就完全落到了表示层。这样势必会增加表示层的代码量,将表示层的任务复杂化,和表示层只负责接受用户的输入并返回结果的任务不太相称,并增加了层与层之间的耦合程度。为了解决这个矛盾,引入一个业务外观(BusinessFacade)层,让它来负责管理系统内部类的调用,并为表示层提供了一个单一而简单的接口。Duwamish 7.0各层的职能分别为:

● 表示层为客户端提供对应用程序的访问。

● 业务外观层为表示层提供处理账户、类别浏览和购书的界面。业务外观层作为隔离层,它将用户界面与各种业务功能的实现隔离开来。除了低级系统和支持功能之外,对数据库服务器的所有调用都是通过此程序集进行的。

● 业务规则层包含各种业务规则和逻辑的实现。业务规则层完成如客户账户和书籍订单的验证这样的任务。

● 数据访问层为业务外观层和业务规则层提供数据服务。

接下来简单介绍一下SOA(面向服务的架构)。

SOA最主要的应用场合在于解决互联网环境下的不同商业应用之间的业务集成问题。互联网环境区别于局域网环境的几个特点主要是:

● 大量异构系统并存,不同计算机硬件工作方式不同,操作系统不同,编程语言也不同。

● 大量、频繁的数据传输,速度仍然相对较缓慢并且不稳定。

● 无法完成服务(service)的版本升级,甚至根本就无法知道互联网上有哪些机器直接或者间接地使用某个服务。

SOA架构分层模型如图1.4所示。

图1.4 SOA架构分层模型

在SOA架构中不同的功能模块可以被分为7层(图中分别编号为1到7)。

● 第1层是系统已经存在的程序资源,例如ERP或者CRM系统等。

● 第2层是组件层,在这一层中用不同的组件把底层系统的功能封装起来。

● 第3层是SOA系统中最重要的服务层,在这层中,要用底层功能组件来构建所需要的不同功能的服务。总的来说,SOA中的服务可以被映射成具体系统中的任何功能模块,但是从功能性方面可以大致划分为以下三种类型:

➢ 商业服务(business service) 或者是商业过程(business process)。这一类的服务是一个企业可以暴露给外部用户或者合作伙伴使用的服务。比如说提交贷款申请、用户信用检查、贷款信用查询等。

➢ 商业功能服务(business function service)。这类服务会完成一些具体的商业操作,也会被更上层的商业服务调用,不过大多数情况下这类服务不会暴露给外部用户直接调用,例如检索用户账户信息、存储用户信息等。

➢ 技术功能服务(technical function service)。这类服务主要完成一些底层的技术功能,比如日志服务、安全服务等。

● 第4层是商业流程层,在这一层中利用已经封装好的各种服务来构建商业系统中的商业流程。

● 第5层是表示层,表示层用来向用户提供用户接口服务,这一层可以用基于portal的系统来构建。

● 第6层是企业服务总线(ESB),上面的1到5层都需要有一个集成的环境来支持它们的运行,ESB提供了这个功能。

● 第7层是基础架构服务,主要为整个SOA系统提供一些辅助的功能,例如服务质量管理、安全管理等。

SOA架构具有一些典型特性,主要包括松耦合性、位置透明性以及协议无关性。

● 松耦合性要求SOA架构中的不同服务之间应该保持一种松耦合的关系,也就是应该保持一种相对独立无依赖的关系。

● 位置透明性要求SOA系统中的所有服务对于它们的调用者来说都是位置透明的,也就是说每个服务的调用者只需要知道他们调用的是哪一个服务,但并不需要知道所调用服务的物理位置在哪里。

● 协议无关性要求每一个服务都可以通过不同的协议来调用。

由于SOA架构具有以上这些特性,因此它的出现为企业系统架构提供了更加灵活的构建方式。如果基于SOA来构建企业系统架构,就可以从底层架构的级别来保证整个系统的松耦合性以及灵活性,这都为未来企业业务逻辑的扩展打好了基础。

由于网站应用架构的种类繁多,因此本书仅以三层结构(最普遍使用的)和SOA (当前最热门的)为例,说明如何针对每一层进行测试。这部分的内容将在第4章中进行详细的阐述。