




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、测试用例的前置条件和后置条件除 了第二点中谈到的数据需要准备外,在测试用例这个Level,必须有一些条件满足,您才能开始执行它。比如准备一个初始设置条件下的IE 浏览器和已安装过老版本该软件的XP系统。这些可重用的准入条件,可以考虑不作为特定用例的Step,而是把它提取出来,作为Setup Section或叫Pre-Condition。对于后置条件或Post- condition,往往我们用它来做一些处理或恢复,比如在上面的取款例子中,如果我们要用相同的帐号重复测试,在正好取完所有金额,余额为零的情况 下,可以通过一些步骤或数据库脚本重置帐号余额。同样,您为某个用例设置浏览器禁用了Cookie
2、,执行完该用例后,是不是也是需要回复到默认设置的状态 呢?集中的把这些步骤整理成一个相对独立的操作单元,具体用例中只要引用就可以了,这样会便于对用例的理解和在多处复用。顺便说一下,对于一些类似软件运行环境的条件,比如安装和配置测试中,需要3种操作系统和3种浏览器的组合等,我们可以把他放在Test Set这个Level上来,不用写多个用例,只是在测试计划和执行的管理系统中作为测试集的一个环境参数,恰当地表达出来就可以。4. 常用业务操作(Knowledge Base)对于一个大型的应用,比如银行系统,开发和测试工作是长期的,持续的一个过程,这样的系统很适合引入自动化测试。它业务逻辑复杂,测试技术
3、性要求高,往往使用了不同厂商的工具和多种脚本语言(如Shell,Python等),也存在了很多可用的遗留脚本。这些完成一些预定业务操作的脚本单元,是可以直接借用的。为了在公司和产品层面,管理好这些可复用的资源,一种好的方式是给它们标上号,如KB_PRJ01_Module02_XXX,集中管理起来,以后的用例中只要调用即可。举 例来说,在银行业务测试中我们,需要模拟和银联的接口,让测试帐号向外汇款,取得响应信息,并保存结果,这可能是个复杂而底层的处理过程,对一般员工是不 需要,也没有权限去深入掌握的。这时,将他们包装成一个个Shell脚本或小工具,做好使用说明和统一建档,在以后的项目测试中,只要
4、调用就可以了。如 此,可以大大提高各个有相关接口的模块的自动化测试工作效率。根据以往工作中常见的一些问题,对于如何写好测试用例(不仅针对自动化测试),做以下做几点补充: 推荐 不推荐 将用例的内容描述清楚,强调怎么操作,验证什么,然后期待的结果是什么。 Copy需求和设计文档中的内容;描述成:什么条件下,逻辑会是怎样。这样对测试用例的阅读和执行人员,不具有可操作性。 期待的结果要写具体,如:系统反应是什么;结果数字是多少;用户被带到什么页面;显示什么成功信息;后台或数据库中该记录的修改后结果是怎么样的。 描述成:”验证系统返回正确结果“;”页面元素显示跟SPEC一致“;”操作成功“等 比较抽象
5、的说法。 业务逻辑性较强的应用软件,做到以业务流为主线,来组织用例。 以页面形式组织用例。 以Module、Function、测试类型、基本业务流、备选业务流的树状结构形式,分层次组织用例;使用用例管理工具。 Word格式的扁平组织结构,不利于管理和阅读。 用一个属性字段,建立用例和Spec等文档的某个章节间的映射。 无法和需求对应,以后难以计算 用例覆盖率,测试执行覆盖率。 每个Module、Function、特定业务的一组测试用例,之间做到独立、没有耦合。 用例之间有依赖,无法做到:挑选30%的用例做回归测试。 在时间和成本允许的情况下,尽量做到:用例粒度为“一种不同的操作,得到不同的结果
6、,就单独写一个用例“。 在用例中的操作步骤中,甚至期待结果中,仍然存在条件分支。 对于复杂的业务操作过程,如”一次顺序的表单签核过程“和”一次完整的信贷手续“,单独增加一些贯穿整个业务流的大型测试用例。 对于一个长业务操作,只存在比较零散的细节用例。 将用例分优先等级,便于在回归测试时挑选核心业务或用户操作密集的用例。 用例 没有优先级和重要程度的定义。 自动化测试用例设计的原则很多公司在实施自动化测试的过程中,往往会把所有的手工测试用例作为自动化测试用例,并且直接进行脚本的开发工作,甚至有些公司不写自动化测试用例,直接想当然地开发测试脚本,这些都是极其不规范的做法,甚至很有可能是导
7、致最后自动化测试项目失败的最大原因。那么问题就来了,为什么不能使用手工测试用例完全替代自动化测试用例呢?有以下几点原因,同时也是自动化测试用例的设计原则 原则1:自动化测试用例的范围往往是核心业务流程或者重复执行率较高的。 在选取自动化测试用例范围时,很多测试工程师或者上级领导可能心里会过分依赖自动化测试,会认为自动化测试就应该覆盖所有的手工测试用例,自动化测试的 覆盖率就应该达到百分之百。其实恰好相反,这样的想法往往会导致自动化测试最终失败。在一些大型项目中,往往测试用例的数量会很庞大,而且如果遇到一些繁 杂的被测程序(特别是C/S架构),脚本开发工作往往会相当耗时间,并且很多测试
8、用例甚至根本就不能通过自动化来实现。举些例子,现在很多公司自动化测试 都是刚起步,对自动化测试的了解程度只是停留在字面上,在公司对测试也不是非常重视的情况下,当然不太愿意去花精力招一个具有自动化测试开发经验的工程 师,很多还是停留在使用工具的录制回放功能来完成自动化测试。正是存在这样的技术限制情况下,往往在实施中,会出现很多录制回放不能解决的问题,测试工具 完全无法识别测试对象,无法识别一些特殊的加密测试控件。还有,如果项目的变更频率,测试用例数量大的话,增加了后期的维护工作量等,都是造成最终失败的 一些隐患。投入越大,损失越大。因此,往往我们会选取最核心的一些业务路径或者是重复执行率较高的一
9、些手工测试用例进行自动化测试,这样能够充分发挥出自 动化测试的优势。 原则2:自动化测试用例的选择一般以“正向”为主。手工测试用例分正常情况和异常情况,在设计的时候,可能往往会去设计很多异常情况来验证程序是否有Bug, 并且一个正常情况的测试用例往往会对应几十个非正常情况的测试用例,而每种异常情况的测试用例都会有各种各样的预期结果。在自动化测试中,很多人喜欢将正 常情况称为“正向”;反之,异常情况则称为“反向”。下面,我们试想以下,如果将这些异常情况全部转化、反应到自动化测试脚本中,那肯定需要非常繁琐的判 断才能做到。这个对于自动化测试工程师来说,其现有的工作量还是今后的脚本维护量都
10、是不可小视的。对于整个自动化测试项目来说,如果每个异常情况都要写进 脚本中,那真的是花了大价钱买一堆小东西,小东西真正能发挥大作用的毕竟很少。因此,真正在自动化测试项目实施中,往往会舍弃反向用例,个别比较重要的除 外。使每个东西都能发挥其最大的作用才是企业最想看到的。功能自动化测试主要还是用于回归测试,回归测试的目的就是保证新增功能后老功能是否能够正常继续 运作。而自动化测试则是让测试人员从繁琐又枯燥的重复手工测试中解放出来,这就是目的和目标。原则3:不是所有手工测试用例都可以使用自动化测试来实现的。 这里纠正许多测试从业人员的一个错误观念,刚接触测试自动化的普遍都会认为手工测试用例全部要转化
11、为自动化测试用例,但是在真正实施的时候,却发现很多 测试用例是自动化无法实现的,或者有些测试用例根本就没有必要去自动化的。例如,有些用例会牵涉到硬件设备辅助的,最简单的例子就是用例执行过程中需要使 用刷卡机才能获取卡号信息(如果有技术能力,当然不排除自行开发接口供测试工具调用,但毕竟能有技术实力做到这一步的不多,能有这样的重视程度的更不 多);再比如,有些测试用例是需要与合作机构进行互动联调,联调时是需要和对方实时沟通,以及根据具体情况给予响应的,这些情况多数还是只能使用手工人为 地来完成。当然,决定是否转化为自动化测试,必须事先有一个规范文档来定义哪些是需要转化为自动化测试哪些是不需要的,否
12、则测试工程师就会不知所措,没有 一个标准。一旦有了这个标准,自动化测试工程师就可以严格按照文档里的流程去完成需要转化部分的自动化测试用例的脚本开发工作了。原则4:手工测试用例可以不用回归原点,而自动化用例往往是必须的。 很多有经验的自动化测试从业人员一定有这样的经历,很多时候脚本写完后,第一次执行没有任何问题,而第二次执行时立刻就会报错,原因就是没有回归原点。 所谓回归原点就是执行的测试用例最终需要恢复其在执行前的初始状态,如果没有回归原点,就会把此脚本称之为死脚本。举个最简单的例子,比如添加用户功能, 我们都知道每个用户名都是唯一的,当写完一个添加用户的脚本之后,执行第一次没有问题,因为执行
13、前此用户还不存在,但是当执行第二次时,程序就会出现用户 重复而报错,此时这个添加用户的脚本就失去了它的价值,在这种情况下,我们就需要在自动化测试用例的最后加上删除这个用户的步骤,这样在下次执行用例时就 不会出现用户重复的情况了。当然,除了回归原点,还可以使用另一种方式进行,那就是初始化数据,比如ATM机取款,假设需要执行取款100元的操作,而银 行卡余额是120元,当测试脚本第一次执行时可能没有任何问题,但是第二次系统就会报余额不足,这样就成为了死脚本,解决方案有两种:一种是直接进行初始 化数据,每次执行用例之前都重置下余额(只需大于100即可);第二种方法可以在用例执行前,先查询下余额是否大
14、于100,若大于等于则继续,若小于则做 一笔充值100的操作,这样即可解决。两种方式可以看具体情况使用,数据初始化方便,但有时候初始化之后可能会影响到其他自动化测试用例的执行,而第二种 方式相对在脚本上需要稍微花点功夫。究竟使用哪种方式还需要具体情况具体分析。总之,在执行自动化测试用例之前做好数据准备,这也是自动化测试的关键步 骤。原则5:自动化测试用例和手工测试用例不同,不需要每个步骤都写预期结果。 在手工测试用例的设计过程中,几乎每一个测试步骤都有一个预期结果。但是,在自动化测试用例的设计中并不采用,在自动化测试用例中,只有准备在测试脚本 中设置成检查点的步骤才有预期结果,其他所有的步骤只
15、将它看作一个步骤,这样做的好处是一目了然、目的明显、层次分明,以后写测试脚本直接跟着自动化测试 用例就行了。因为经过前面的探讨应该已经知道,自动化测试中并不是所有的东西都需要验证的。所以,作者在前面的章节中也提到过,基本上手工测试用例多多少 少都要进行一些转换的,就是因为它们之间的格式是不一致的。举一个简单的例子,假设需要设计一个注册页面的自动化测试用例,有10几个表单 需要填写,在手工测试用例中,每个表单的填写都一定会有预期结果,因为它的确在检查每一项是对了还是错了,只是用的是你的眼睛在检查而已,所以速度非常的 快,甚至你自己潜意识都忽略了其实你已经检查了。但是,在自动化测试中,我们知道如果
16、你要检查,那一定需要写代码,如果每项都检查,那代码量有多大是可想 而知的,不是说做不到,只是这样做根本不符合自动化测试的特点。所以,绝大部分时候,这些在自动化测试中可有可无的检查,我们全部“不检查”,只当做一个业务流程和步骤,是不需要设立预期结果的。难以对于UI样式或UI逻辑进行断言以上图为例,有一个UI样式类的缺陷(左侧菜单树的根节点“console”下面多出来一条虚 线)和一个UI逻辑类的缺陷(右侧用户列表只有一页,但“下一页”和“最后一页”图标依然是可以点击的,即没有灰显)。此类缺陷即使对于经验丰富、心思缜 密的测试人员,在人工测试时也是很可能发现不了的,并且在自动化测试过程中也很难进行
17、断言。即使存在上述问题,测试脚本中是否有充分的断言,依然是评判自动化测试有效性的一个重要指标。但实施过自动化测试的人应该都会有这样的体会:“大部分断言在大部分情况下只是佐证软件是运行正常的”。当然,所有人都应该是非常期待看到这样的结果,毕竟谁也不希望每次回归测试时都是用例大面积不通过。只是辛辛苦苦写这些断言语句的测试人员心里未免有些“小遗憾”。本系列上篇文章中谈到“很多人一提到自动化测试脚本,马上就想到需要提供录制工具”,但如果换个角度思考,很可能就是“柳暗花明又一村”。在这里,我们同样换个角度思考,假设我们的自动化测试主要目标是为了证明软件运行正常,那么我们会怎么做?笔者这边的一个经验就是“
18、按照完整的业务流程来组织测试用例,只对少量、必要的关键点进行断言”。以“租户对虚拟化资源的申请使用”为例,来具体看看测试用例的组织方式:1. 新租户注册;2. 管理员登录系统,对注册租户进行审批,然后退出系统;3. 审批后的租户登录系统;4. 租户申请所需要的虚拟化资源(比如,40G硬盘、2核CPU、2G内存),然后退出系统;5. 管理员登录系统,对租户申请的资源进行审批,然后退出系统;6. 租户登录系统,在已申请资源的基础上创建安装指定操作系统的虚拟机;7. 断言虚拟机是否创建成功;8. 租户退出系统;9. 管理员登录系统,删除租户;10. 断言租户之前申请的资源是否被完全释放;11. 租户
19、再次登录系统,断言是否无法登录;上述测试用例就是按照完整的业务流程进行组织,并且只对少量关键点(7、10、11)进行断言,如果整个用例可以运行通过,就能证明这个业务是没有问题的。另外还有一个值得考虑的现象,就是相对于自动化测试而言,一个优秀的测试人员在人工测试时是如何判断功能正确与否的呢?他不会死板的只盯着某几个输 入域的值,他一定还会同时关注页面上所有数据的正确性、会更加关注业务流程是否正确、会更敏锐的发现页面样式或UI逻辑类的缺陷。为了兼顾“证明软件正常运行”和“人性化的识别软件缺陷”,一个优秀的测试工具应该考虑提供以下多种断言机制。一、 控件级细粒度断言即前面提到的最常见的断言方式。在测
20、试过程中,可以在任何位置增加断言脚本,来判断页面指定控件是否存在、控件显示值是否为预期结果等。通常建议只对关键校验点进行断言。二、 页面级粗粒度断言通过对比前(之前测试通过)后(后续持续发布)版本在测试用例路径和输入参数相同的情 况下,整个页面内容(包括截图和数据)是否严格相同来做粗粒度断言。通过页面截图进行断言有两个实现要点:首先要选择一个合适的截图方案(笔者推荐采用Selenium WebDriver提供的TakesScreenshot接口);其次需要提供图片对比工具,以便测试人员可以一眼看出两个版本页面截图的差异。下面是笔者在测试框架中实现的截图自动化对比功能的实际效果。下图中左侧部分是
21、“实际结果截图”、右侧是“预期结果截图”、中间部分是差异对比,测 试人员一眼便可看出其中的Bug:“表格行选中的翻页缓存(在当前页选中几条记录,翻到下一页再翻回本页,需要保持之前的行选中状态)功能丢失了”。通过页面数据进行断言的实现方式相对简单一些,首先要提取页面上所有的数据(或文本),接着进行格式化,然后再自动化对比。 “页面级粗粒度断言”的特点及应用场景如下:· 无需编写任何断言语句;· 需要能够提供可用于自动对比的历史正确版本,特别适用于可以持续构建的项目;· 能判断出UI样式和UI逻辑类的错误;· 由于对比绝对精准,导致可能存在误判,因此需要人工
22、对差异图片进行排查; 【注】由于很多Web页面都有渐入渐出、点击时按钮变色等很炫的效果,所以在两次截图的瞬间可能页面的动态样式是不一样的,这就是所谓的“误判”。笔者对 于一个“动态样式”适中的项目采用这种断言方式,统计结果表明误判率在20%左右。· 鉴于回归测试的时候,通常大部分用例应该是可以通过的,所以“页面级粗粒度断言”的投入产出比非常占优势!三、 基于业务逻辑断言在测试设计时把有依赖关系的用例一起执行,如果某个步骤出现问题,即便不设置任何断言语句,在当前步骤或后续步骤的测试用例也会执行失败。下面以“增加、查询、修改、删除”这个最典型的流程来说明(如下图所示)。假定在“增加”环节
23、出现问题,那么我们的测试用例执行情况可能出现如下几种结果:1. 如果在“增加记录A”的用例中包含对是否增加成功的断言,那么测试用例从“增加记录A”开始出错;2. 如果在“增加记录A”中不包含断言,而是在“查询A”的用例中包含是否有查询结果的断言,那么测试用例会从“查询A”开始出错;3. 如果在“查询A”中也不包含断言,那么测试用例会从“修改查询结果”开始出错。所谓“基于业务逻辑断言”,就是指上述第三种情况,其特点及应用场景如下:· 无需编写任何断言语句,但测试设计要考虑业务逻辑顺序;· 与“页面级粗粒度断言”相比,不需要提供可用于对比的历史正确版本,通常适用于项目刚开发或样
24、式做整体调整等情况;· 断言错误的位置不精准,可能延后;· 执行过程每一步都做截图备份(通过Selenium WebDriver可以很方便的实现),可以非常有效的辅助定位准确的出错原因;· 鉴于回归测试的时候,通常大部分用例应该是可以通过的,所以“基于业务逻辑断言”的投入产出比非常占优势!四、 自定义扩展断言在人工测试时经常有些操作结果的正确与否在当前页面无法做出判断,需要到其它页面甚至系统外部(比如,数据库、输出日志)获取信息来做出判断。以最常见的“基于数据库进行断言”为例,测试工具需要支持把断言时用到“预期结果”和“实际结果”配置为对应的SQL语句。以上介绍了
25、从测试工具的角度可以提供的多种断言机制,在自动化测试过程中应该根据项目实际情况,考虑采用上述多种断言的组合,以弥补控件级细粒度断言的不足。ASP.NET MVC集成EntLib实现“自动化”异常处理实例篇 个人觉得异常处理对于程序员来说是最为熟悉的同时也是最难掌握的。说它熟悉,因为仅仅就是try/catch/finally而已。说它难以掌握,则是因为很多开发人员却说不清楚try/catch/finally应该置于何处?什么情况下需要对异常进行日志记录?什么情况下需要对异常进行封装?什么情况下需要对异常进行替换?对于捕获的异常,在什么情况下需要将其再次抛出?什么情况下则不需要?合理的异常处理应该
26、是场景驱动的,在不同的场景下,采用的异常处理策略往往是不同的。异常处理的策略应该是可配置的,因为应用程序出现怎样的异常往往是不可预测的,现有异常策略的不足往往需要在真正出现某种异常的时候才会体现出来,所以我们需要一种动态可配置的异常处理策略维护方式。目前有一些开源的异常处理框架提供了这种可配置的、场景驱动的异常处理方式,EntLib的Exception Handling Application Block(以下简称EHAB)就是一个不错的选择。源代码从这里下载本文已经同步到How ASP.NET MVC Works?中目录 一、通过指定Handle-Error-Action响应请求 二、通过E
27、rror View显示错误消息 三、自动创建JsonResult响应Ajax请求一、通过指定Handle-Error-Action响应请求在正式介绍如何通过扩展实现与EntLib以实现自动化异常处理之前,我们不妨先来体验一下异常处理具有怎样的“自动化”特性。以用户登录场景为例,我们在通过Visual Studio的ASP.NET MVC项目模板创建的Web应用中定义了如下一个简单的数据类型LoginInfo封装用户登录需要输入的用户名和密码。 1: public class LoginInfo 2: 3: DisplayName("用户名") 4: Required(Err
28、orMessage="请输入0") 5: public string UserName get; set; 6: 7: DisplayName("密码") 8: Required(ErrorMessage = "请输入0") 9: DataType(DataType.Password) 10: public string Password get; set; 11: 然后我们定义了如下一个HomeController。基于HTTP-GET的Action方法Index将会呈现一个用户登录View,该View使用创建的Logi
29、nInfo对象作为其Model。真正的用户验证逻辑定义在另一个应用了HttpPostAttrubute特性的Index方法中:如果用户名不为Foo,抛出InvalidUserNameException异常;如果密码不是“password”,则抛出InvalidPasswordException异常。InvalidUserNameException和InvalidPasswordException是我们自定义的两种异常类型。 1: ExceptionPolicy("defaultPolicy") 2: public class HomeController : Extende
30、dController 3: 4: public ActionResult Index() 5: 6: return View(new LoginInfo(); 7: 8: 9: HttpPost 10: HandleErrorAction("OnIndexError") 11: public ActionResult Index(LoginInfo loginInfo) 12: 13: if (string.Compare(loginInfo.UserName, "foo", true) != 0) 14: 15: throw new In
31、validUserNameException(); 16: 17: 18: if (loginInfo.Password != "password") 19: 20: throw new InvalidPasswordException(); 21: 22: return View(loginInfo); 23: 24: 25: HttpPost 26: public ActionResult OnIndexError(LoginInfo loginInfo) 27: 28: return View(loginInfo); 29: 30: 上面定义的
32、HomeController具有三点与自动化异常处理相关的地方:· HomeController继承自自定义的基类ExtendedController,后者完成了对异常的自动化处理。 · HomeController类型上应用了自定义的ExceptionPolicyAttribute特性用于指定默认采用的异常处理策略名称(“defaultPolicy”)。 · 基于HTTP-POST的Index方法上应用了HandleErrorActionAttribute特性用于指定一个Handle-Error-Action名称,当异常在目标Action执行过程中抛出并通过EH
33、AB处理后,指定的Action会被执行以实现对请求的响应。对于我们的例子来说,从Index方法抛出的异常被处理后会调用OnIndexError方法作为对当前请求的响应。 下面是代表登录页面的View的定义,这是一个Model类型为LoginInfo的强类型View。在该View中,作为Model的LoginInfo对象以编辑默认呈现在一个表单中,表单中提供了一个“登录”提交表单。除此之外,View中还具有个ValidationSummary。 1: model LoginInfo 2: <html> 3: <head> 4: <title>用户登录</
34、title> 5: <style type="text/css"> 6: .validation-summary-errorscolor:Red 7: </style> 8: </head> 9: <body> 10: using (Html.BeginForm() 11: 12: Html.ValidationSummary(true) 13: Html.EditorForModel() 14: <input type="submit" value="登录" /> 1
35、5: 16: </body> 17: </html>通过HomeController的定义我们知道两种不同类型的异常(InvalidUserNameException和InvalidPasswordException)分别在输入无效用户名和密码是被抛出来,而我们需要处理的就是这两种类型的异常。正对它们的异常处理策略定义在如下的配置中,策略名称就是通过应用在HomeController上的ExceptionPolicyAttribute特性指定的“defaultPolicy”。 1: <configuration> 2: <configSections&
36、gt; 3: <section name="exceptionHandling" 4: type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" /> 5: </configSections> 6: <exceptionHandling> 7: <exce
37、ptionPolicies> 8: <add name="defaultPolicy"> 9: <exceptionTypes> 10: <add type="MvcApp.InvalidUserNameException, MvcApp" postHandlingAction="ThrowNewException" name="InvalidUserNameException"> 11: <exceptionHandlers> 12: <add name
38、 ="ErrorMessageHandler" type="MvcApp.ErrorMessageHandler, MvcApp" errorMessage="用户名不存在"/> 13: </exceptionHandlers> 14: </add> 15: 16: <add type="MvcApp.InvalidPasswordException, MvcApp" postHandlingAction="ThrowNewException" n
39、ame="InvalidPasswordException"> 17: <exceptionHandlers> 18: <add name ="ErrorMessageHandler" type="MvcApp.ErrorMessageHandler, MvcApp" errorMessage="密码与用户名不匹配"/> 19: </exceptionHandlers> 20: </add> 21: </exceptionTypes> 22: <
40、;/add> 23: </exceptionPolicies> 24: </exceptionHandling> 25: . 26: </configuration>通过上面的这样异常策略配置可以看到:我们使用一个自定义的名为ErrorMessageHandler的ExceptionHandler来处理抛出来的InvalidUserNameException和InvalidPasswordException异常,而ErrorMessageHandler仅仅是指定一个友好的错误消息,该消息一般会呈现给最终的用户。运行该程序后一个用于登录页面会呈现出来,当
41、我们输入错误的用户名和密码的时候,相应的错误消息(在配置中通过ErrorMessageHandler设置的错误消息)会以如图7-16所示的效果显示出来,其实整个View是通过执行Action方法OnIndexError返回的ViewResult呈现出来的。二、通过Error View显示错误消息除了通过执行对应的Handle-Error-Action来呈现异常处理后的最终结果之外,还支持错误页面的错误呈现方法。简单起见,我们只是用名称为Error的View来作为最终的错误页面。为了演示基于错误页面的呈现方式,我们按照如下的方式重新定义了ViewsShared目录下的Error.cshtml。
42、1: model ExtendedHandleErrorInfo 2: 3: Layout = null; 4: 5: <!DOCTYPE html> 6: <html> 7: <head> 8: <meta name="viewport" content="width=device-width" /> 9: <title>Error</title> 10: <style type="text/css"> 11: h3 color:Red; 12: &
43、lt;/style> 13: </head> 14: <body> 15: <h3> 16: Html.DisplayFor(m=>m.ErrorMessage) 17: </h3> 18: <ul> 19: <li>Controller: Html.DisplayFor(m => m.ControllerName)</li> 20: <li>Action: Html.DisplayFor(m => m.ActionName)</li> 21: <li>
44、;Exception: 22: <ul> 23: <li>Message: Html.DisplayFor(m => m.Exception.Message)</li> 24: <li>Type: Model.Exception.GetType().FullName</li> 25: <li>StackTrace: Html.DisplayFor(m => m.Exception.StackTrace)</li> 26: </ul> 27: </li> 28: </ul
45、> 29: </body> 30: </html>上面这个View的Model类型是具有如下定义的ExtendedHandleErrorInfo。它继承自HandleErrorInfo,只额外定义了一个表示错误消息的ErrorMessage属性。在上面的这个View中,我们将错误消息、异常类型和StackTrace和当前Controller/Action的名称呈现出来。 1: public class ExtendedHandleErrorInfo : HandleErrorInfo 2: 3: public string ErrorMessage get; pr
46、ivate set; 4: public ExtendedHandleErrorInfo(Exception exception, string controllerName, string actionName, string errorMessage) 5: : base(exception, controllerName, actionName) 6: 7: this.ErrorMessage = errorMessage; 8: 9: 当利用EntLib的EHAB对从Index方法中抛出的异常进行处理后采用错误View的方式来响应请求,我们需要按照如下的方式将应用在该方法上的Handl
47、eErrorActionAttribute特性注释掉。 1: ExceptionPolicy("defaultPolicy") 2: public class HomeController : ExtendedController 3: 4: /其他成员 5: HttpPost 6: /HandleErrorAction("OnIndexError") 7: public ActionResult Index(LoginInfo loginInfo) 8: 9: /省略实现 10: 11: 再次运行该程序并分别输入错误的用户名和密码后,默认的错误View
48、(Error.cshtml)将会以如下图所示地效果把处理后的异常结果呈现出来。三、自动创建JsonResult响应Ajax请求用于实施认证的Action方法Index可以通过普通的HTTP-POST的形式来调用,同样也可以通过Ajax请求的方式来调用。对于Ajax请求来说,我们最终会将通过EntLib处理后的异常封装成如下一个类型为ExceptionDetail的对象。如下面的代码片断所示,ExceptionDetail具有与Exception对应的属性设置。最终根据抛出异常对象创建的ExceptionDetail对象会被用于创建一个JsonResult对象对当前Ajax请求予以响应。 1:
49、public class ExceptionDetail 2: 3: public ExceptionDetail(Exception exception,string errorMessage=null) 4: 5: this.HelpLink = exception.HelpLink; 6: this.Message = string.IsNullOrEmpty(errorMessage) ? exception.Message : errorMessage; 7: this.StackTrace = exception.StackTrace; 8: this.Type = excepti
50、on.GetType().ToString(); 9: if (exception.InnerException != null) 10: 11: this.InnerException = new ExceptionDetail(exception.InnerException); 12: 13: 14: 15: public string HelpLink get; set; 16: public ExceptionDetail InnerException get; set; 17: public string Message get; set; 18: public str
51、ing StackTrace get; set; 19: public string Type get; set; 20: 当客户端接收到回复的Json对象后,可以通过检测其是否具有一个ExceptionType属性(对于一个ExceptionDetail对象来说,该属性不可能为Null)来判断是否发生异常。作为演示我们对Action方法Index对应的View进行了如下的改动。 1: model LoginInfo 2: <html> 3: <head> 4: <title>用户登录</title> 5: <script type=&qu
52、ot;text/javascript" src="Url.Content("/Scripts/jquery-1.6.2.js")"></script> 1: 2: <script type="text/javascript" src="Url.Content("/Scripts/jquery.unobtrusive-ajax.js")"> 1: </script> 2: <script type="text/java
53、script"> 3: function login(data) 4: if (data.ExceptionType) 5: alert(data.Message); 6: 7: else 8: alert("认证成功"); 9: 10: 11: </script> 6: </head> 7: <body> 8: 9: AjaxOptions options = new AjaxOptionsOnSuccess = "login" 10: 11: using (Ajax.BeginForm(option
54、s) 12: 13: Html.EditorForModel() 14: <input type="submit" value="登录" /> 15: 16: </body> 17: </html>如上面的代码片断所示,我们通过调用AjaxHelper的BuginForm生成了一个以Ajax形式提交的表单。表单成功提交(服务端因对抛出的异常进行处理而返回一个封装异常的Json对象,对于提交表单的Ajax请求来说依然属于成功提交)后会调用我们定义的回调函数login。在该JavaScript函数中,我们通过得到的对象是否
55、具有一个ExceptionType属性来判断服务端是否抛出异常。如果抛出异常,在通过调用alert方法将错误消息显示出来,否则显示“认证成功”。我们再次运行我们的程序并分别输入不合法的用户名和密码,相应的错误消息会以对话框的形式显示出来,具体的显示效果如下图所示。对于一个新项目,QA通常会首先为新特性创建手工测试用例,为了之后维护方便,也通常将这些用例存放在一张Excel表或者一个专门的测试用例管理 系统里。而在项目进行过程中或之后,具备自动化测试能力的QA团队会将手工测试用例转化为代码,加入套件(Suite)中,用于之后的回归。以往我们认为手工测试用例与自动化代码之间存在联系,但并不紧密:&
56、#183; 手工测试用例文档很容易阅读,可以帮助学习业务,但因为维护不够灵活,很难跟上快速的变化。依赖手工测试用例对项目进行回归又是及其痛苦的。· 自动化测试代码可以很明显的提升效率,但不容易阅读。因为人们通常缺少更新代码注释的动力(没什么外人会用到,老鸟又不依赖它),久而久之我们不知道那一堆自动化用例究竟测了些什么,导致通过率逐步走低,又无人维护。自动化测试最终土崩瓦解。这似乎是一种宿命般的失败。有些团队希望建立自动化测试体系,却从一开始就遇到类似的问题,导致进展缓慢,无法持续向老板秀出效果,最终又退缩回原点。原因是什么?怎么去破解这个困局呢?1. 用例文档不应该与自动化代码分离,而应存在于代码中,随着代码的变化而及时更新。2. 用例文档应该简洁,可以自我组织与管理,并以一种清晰的结构被展现和分享。3. 自动化测试用例的运行历史应该被测量和记录,数据可以集中
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 内外架合同范例
- 化肥合作合同范例
- 专项经理聘用合同范本
- 农业购货合同范本
- 化工产品购销服务合同范本
- 医院购销合同范本
- 出口布料销售合同范例
- 养殖水车出租合同范例
- 农村田租合同范本
- cpc广告合同范本
- 设备维修的基本技能培训
- 2025年中国邮政招聘笔试参考题库含答案解析
- 人教版(2024)七年级英语上册新教材的变化及教学建议课件
- 2025年中考语文一轮复习:九年级上册知识点梳理
- 2025年新闻部工作计划
- 中国近代史纲要西安财经大学练习题复习资料
- 中国成人ICU镇痛和镇静治疗指南解读
- 2023年工程质量监督人员考试真题模拟汇编(共957题)
- 延长保修服务合同
- 2025中考英语作文19个热点话题及范文
- 2023三年级英语下册 Unit 1 How are you第3课时说课稿 湘少版
评论
0/150
提交评论