Error

C++博客 首页 新随笔 联系 聚合 管理
  217 Posts :: 61 Stories :: 32 Comments :: 0 Trackbacks

3.1调试测试

×我们必须创建测试以重现问题

×我们必须多次运行测试以简化问题

×我们必须重新运行测试以观察运行过程

×我们必须重新运行测试以验证修改是否成功

×每个版本发布之前,我们必须重新运行测试,以便发现将来不会再次出现,这种称为回归测试

在调试过程中需要频繁的进行自动测试,应此最好尽可能的采用自动化测试。通常通过使用自动化测试技术,可以更容易的进行全面测试,自动化测试的好处在于:

×可以重用已有测试

×可以进行一些困难的、无法手工执行的测试(如:大规模的随机性测试)

×重复测试

×增强对软件的信心

 

3.2控制程序

        通常,自动化测试必须模拟程序所处的环境---也就是说,测试必须提供程序的输入,并且评估程序的输出。但是模拟环境需要很多技巧,如果环境中包括和程序进行交互的用户,自动测试就必须模拟真实的用户(包括他们的所有能力)。

        通过区分不同的接口,可以避开部分模拟难题,从而使得控制和评估都更易于自动化。下图是典型的三层接口划分:

×表现层处理和用户(或者构建程序环境的任何事物)之间的交互

×功能层封装程序的实际功能,功能独立于表现层

×单元层把功能分解成多个单元,这些单元相互协作形成一个整体

image

 

 

3.3在表现层测试

3.3.1低级交互

        在最低级的抽象级别,用户输入被看做鼠标和键盘的事件流,这种事件流可以是被捕获和重放,即用时间记录器的时间流替代实际输入设备的事件流。

(PS:如果是在windows上测试需要UI交互的程序,貌似可以直接简单的试用key_event mouse_event等几个api就能模拟时间送进来了;当然,还有socket类的测试,管道什么的,,,)

image

3.3.2系统级交互(高阶主题,主要是将在系统(如虚拟机)级别模拟外部操作)

 

3.3.3高级交互

        使用更高级抽象级别的事件流(或者脚本)来模拟外部操作。比如图形程序的测试,以前是直接算坐标控制,现在用更聪明的手段(标识button的text,输入指定的语句而不是裸的**_event)。简而言之就是输入更加具有逻辑性,更加接近“人工智能”

image

 

3.3.4 评估测试结果

        不管是事件流还是通过用户控件控制应用程序,都存在一个重要的问题:模拟环境必须检查程序的输出。

×必须通过检查输出进行同步,应为模拟用户可能会一直等待直到一个特定的动作结束。

×必须通过检查程序输出来进行结果评估,测试的最终目的是确定结果是否和我们的预期相符。

        表现层测试的优点是:它总能实现。我们总是可以模拟和自动执行用户的行为。但是,这是唯一的优点。通常表现层测试只是用于:

×问题发生在表现层

×计算机程序可以很方便的调用表现层

×没有其他的选择(如:由于表现层和功能层没有被清晰的分离,或者无法在较低层次上进行测试)

界面对人越友好,它对计算机程序就越不友好。。。所以表现层测试应该不是最适合自动化测试的接口(至少对非表示层coder来说)

 

3.4在功能层测试

        相比较于模拟用户交互,更加可取的方法是为程序设计一个适合于进行自动化的接口---或者通俗的说,设计接口时要考虑和测试系统的交互。比如通过提供脚本语言接口,脚本语言允许最终用户或者测试人员通过简单的方式自动执行某些任务。

        在功能层进行测试的最大优点是:很容易获取和评估结果。但是,这种测试的前提条件是能清晰的分离表现层和功能层。而一些陈旧的程序都是独立的整体,没有进行表现层和功能层的分离。这种情况下,有三种选择:

×继续在表现层恶心的测试,然后继续纠结

×重新进行大幅度的重新设计,以分离表现层和功能层,或者至少减少他们之间的依赖关系

×分解程序,并且直接测试独立的单元

 

3.5在单元层测试

        任何复杂的程度都可以分解成大量独立的单元---子程序、函数、库、模块、抽象数据类型、对象、类、包、组建、beans或者设计方案和语言提供的任何分解机制。单元之间通过接口同学---就像程序之间通过他们所处的环境进行通信一样。

        现在的想法不是需要自动运行整个程序,而是自动运行某个特定的单元。其优点是自动运行分离的单元一般都比自动运行整个程序容易的多。当然,缺点是自能自动化某个特定的单元行为,于是必须考虑在分离单元的过程中引入的问题。

        最终用户通常是不能访问单元的,因此不可能通过用户脚本来执行系统的功能。但是,程序员可以使用外围程序访问服务的方式访问单元(PS: 这句有点绕口,感觉是让程序员自己去写code做单元测试)

        所有的单元测试工具都提供了一个能组织大量单独测试用例的测试框架---每个测试覆盖一个独立的单元。单元测试应该能在没有任何用户交互的情况下自动运行,测试框架会按照要求运行部分或者所有的单元测试,然后概要显示运行单元测试以及各自的输出结果。运行某一个单元测试时,测试框架会按照以下三个步骤进行:

×建立单元测试以及运行的周边环境。通常一个单元可能需要其他单元或者操作环境的服务。该步骤建立起能使测试运行的环境。

×执行单元测试。每个测试用例覆盖该单元的一个可能的行为,用例首先执行所有操作,然后验证输出是否与预期相符。

×重新清理测试环境。

 

3.6分离单元

        有一些程序的功能层依赖于表现层,根本不可能把他们分离。比如print_to_file,把当前网页打印到文件中。为了防止覆盖已经存在的文件,会请求用户确认是否已经存在。(这个其实也可以使用的代码搞定,麻烦点,不过“确认”这个功能的测试就是必须UI和用户参与)

image

×表现层依赖功能层,应为他需要调用print_to_file()

×功能层依赖表现层,应为他需要调用confirm_loss()

问题就来了:如何切断依赖关系,使单元可以更好分离?

对于这个例子处理起来比较容易。可以让函数按照两种方式运行:自动模式禁止用户确认功能,总是返回true;交互模式,打开用户确认功能,等待用户回答。更加通用的方式是:参数化print_to_file函数,使他能与不同的表现层工作。

image

(PS:其实还可以通过引入一个中间层来解决,假设给print_to_file()加上参数,然后core不再依赖pressntation,而是依赖一个消息模块、或者控制模块,那么只要写一个模拟发送消息、模拟控制的模块即可。解决循环依赖直接的办法是引入中间层)

 

3.7为调试而设计

        依赖抽象而不是具象这一原则对于减少依赖关系有很大帮助。实际上可以利用这种方法创建整个应用程序框架,其中最流行的一个例子就是模型-视图-控制器架构模式,该模式能从应用程序级上解除功能层和表现层之间的耦合。

image

 

        如何创建一个这样的系统?最关键的还是分离功能层和表现层。我们决不希望核心功能依赖某个特定视图。MVC模式就是解决这类问题的一个通用方案。他把职责分解为两个部分:

×模型管理核心数据,并且提供处理这些核心数据的服务。

×个总观察者注册或者粘附(attach)在模型上,核心数据一旦发生变化,他们就会得到通知。

观察者又可以分成两种类型:

×视图负责以特定的方式显示核心数据

×控制器负责处理输入时间并调用模型服务

用户和控制器交互是,他最终可能会调用一个改变核心数据的服务。这是注册在模型上的所有视图都会得到通知。也就是说,他们能从模型那里获得数据以及更新显示。从而用户也就得到反馈。

MVC给测试和调试带来的好处:对于测试来说可以创建和添加新的控制器来调用模型提供的服务---例如,能自动记录执行这些服务的控制器。对于调试来说,可以支持特殊的视图来记录模型的所有变化。最后,可以单独的检查每一个观察者和模型,减少复杂性。

image

 

 

3.8预防未知问题

posted on 2013-03-03 15:18 Enic 阅读(259) 评论(0)  编辑 收藏 引用 所属分类: why programs fail - 系统优化调试指南

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理