发布新日志

  • 测试经验之-测试要素

    2009-10-13 23:48:14

    介绍了单元测试环境配置测试、代码测试、UI测试需要注意的地方。

    基本测试要点


    本文档主要介绍了单元测试环境配置测试、代码测试、UI测试需要注意的地方。

    一、单元测试环境配置测试

    1) 网络连接是否正常

    2) 网络流量负担是否过重

    3) 软件测试平台是否可选

    4) 是否在不同的软件测试平台进行软件测试

    5) 所选软件测试平台的版本(包括 Service Pack )是否正确

    6) 所选软件测试平台的参数设置是否正确

    7) 所选软件测试平台上正在运行的其它程序是否会影响测试结果

    8) 画面的分辨率和色彩设定是否正确

    9) 对硬件测试平台的要求和支持程度

    二、代码测试

    A 静态测试

    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) 程序中的 Begin … End 和 Do … While 等语句中, End 是否对应

    34) 程序、模块、子程序和循环是否能够终止

    35) 是否存在永不执行的循环

    36) 是否存在多循环一次或少循环一次的情况

    37) 循环变量是否在循环内被错误地修改

    38) 多分支选择中,索引变量是否能超过可能的分支数 ,如果超过可能的分支数 , 该情况是否能够得到正确处理

    39) 全局变量定义和用法在各个模块中是否一致

    40) 是否修改了只作为输入用的参数

    41) 常量是否被作为形式参数进行传递

    B 动态测试

    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. Cancel 之类的按钮按下后,控件中的数据是否清空复原或按既定规约处理

    33. Submit 之类的按钮按下后,数据是否得到提交或按既定规约处理

    34. 异常信息表述是否正确

    35. 软件是否按预期方式处理错误

    36. 文件或外设不存在的情况下是否存在相应的错误处理

    37. 软件是否严格的遵循外设的读写格式

    38. 产生的文件和数据表的格式是否正确

    39. 产生的文件和数据表的计算结果是否正确

    40. 打印的报表是否符合既定的格式

    41. 错误日志的表述是否正确

    42. 错误日志的格式是否正确

    三 GUI 测试

    1. 窗体是否能够基于相关的输入或菜单命令适当的打开

    2. 窗体是否能够改变大小、移动和滚动

    3. 窗体的数据是否能够利用鼠标、功能键、方向箭头和键盘操作

    4. 当窗体被覆盖并重新调用后,窗体是否能够正确再生

    5. 窗体相关的功能是否可以操作

    6. 是否显示相关的下拉菜单、工具条、滚动条、对话框、按钮、图标和其他控制,既能正确显示又能调用

    7. 显示多窗体时,窗体名称是否能够正确表示

    8. 活动窗体是否能够被反显加亮

    9. 多用户联机时所有窗体是否能够实时更新

    10. 鼠标无规则点击时是否会产生无法预料的结果

    11. 窗体声音及提示是否符合既定编程规则

  • 单元测试全攻略

    2009-10-13 23:45:40

      一 单元测试概述

      工厂在组装一台电视机之前,会对每个元件都进行测试,这,就是单元测试。

      其实 我们每天都在做单元测试。你写了一个函数,除了极简单的外,总是要执行一下,看看功能是否正常,有时还要想办法输出些数据,如弹出信息窗口什么的,这,也 是单元测试,这种单元测试称为临时单元测试。只进行了临时单元测试的软件,针对代码的测试很不完整,代码覆盖率要超过70%都很困难,未覆盖的代码可能遗 留大量的细小的错误,这些错误还会互相影响,当BUG暴露出来的时候难于调试,大幅度提高后期测试和维护成本,也降低了开发商的竞争力。可以说,进行充分 的单元测试,是提高软件质量,降低开发成本的必由之路。

      对于程序员来说,如果养成了对自己写的代码进行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。

      要进行充分的单元测试,应专门编写测试代码,并与产品代码隔离。老纳认为,比较简单的办法是为产品工程建立对应的测试工程,为每个类建立对应的测试类,为每个函数(很简单的除外)建立测试函数。首先就几个概念谈谈老纳的看法。

       一般认为,在结构化程序时代,单元测试所说的单元是指函数,在当今的面向对象时代,单元测试所说的单元是指类。以老纳的实践来看,以类作为测试单位,复 杂度高,可操作性较差,因此仍然主张以函数作为单元测试的测试单位,但可以用一个测试类来组织某个类的所有测试函数。单元测试不应过分强调面向对象,因为 局部代码依然是结构化的。单元测试的工作量较大,简单实用高效才是硬道理。

      有一种看法是,只测试类的接口(公有函数),不测试其 他函数,从面向对象角度来看,确实有其道理,但是,测试的目的是找错并最终排错,因此,只要是包含错误的可能性较大的函数都要测试,跟函数是否私有没有关 系。对于C++来说,可以用一种简单的方法区隔需测试的函数:简单的函数如数据读写函数的实现在头文件中编写(inline函数),所有在源文件编写实现 的函数都要进行测试(构造函数和析构函数除外)。

      什么时候测试?单元测试越早越好,早到什么程度?XP开发理论讲究TDD,即测 试驱动开发,先编写测试代码,再进行开发。在实际的工作中,可以不必过分强调先什么后什么,重要的是高效和感觉舒适。从老纳的经验来看,先编写产品函数的 框架,然后编写测试函数,针对产品函数的功能编写测试用例,然后编写产品函数的代码,每写一个功能点都运行测试,随时补充测试用例。所谓先编写产品函数的 框架,是指先编写函数空的实现,有返回值的随便返回一个值,编译通过后再编写测试代码,这时,函数名、参数表、返回类型都应该确定下来了,所编写的测试代 码以后需修改的可能性比较小。

      由谁测试?单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成,也就是说,经过了单元测试的代码才是已完成的代码,提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。

       关于桩代码,老纳认为,单元测试应避免编写桩代码。桩代码就是用来代替某些代码的代码,例如,产品函数或测试函数调用了一个未编写的函数,可以编写桩函 数来代替该被调用的函数,桩代码也用于实现测试隔离。采用由底向上的方式进行开发,底层的代码先开发并先测试,可以避免编写桩代码,这样做的好处有:减少 了工作量;测试上层函数时,也是对下层函数的间接测试;当下层函数修改时,通过回归测试可以确认修改是否导致上层函数产生错误。

      二 测试代码编写

      多数讲述单元测试的文章都是以Java为例,本文以C++为例,后半部分所介绍的单元测试工具也只介绍C++单元测试工具。下面的示例代码的开发环境是VC6.0。

      产品类:

        class CMyClass

        {

        public:

        int Add(int i, int j);

        CMyClass();

        virtual ~CMyClass();

      private:

        int mAge; //年龄

        CString mPhase; //年龄阶段,如"少年","青年"

        };

      建立对应的测试类CMyClassTester,为了节约编幅,只列出源文件的代码:

        void CMyClassTester::CaseBegin()

        {

        //pObj是CMyClassTester类的成员变量,是被测试类的对象的指针,

        //为求简单,所有的测试类都可以用pObj命名被测试对象的指针。

        pObj = new CMyClass();

        }

      void CMyClassTester::CaseEnd()

        {

        delete pObj;

        }

      测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象,每个测试用例的开头都要调用CaseBegin(),结尾都要调用CaseEnd()。

      接下来,我们建立示例的产品函数:

        int CMyClass::Add(int i, int j)

        {

        return i+j;

        }

        和对应的测试函数:

        void CMyClassTester::Add_int_int()

        {

        }

      把参数表作为函数名的一部分,这样当出现重载的被测试函数时,测试函数不会产生命名冲突。下面添加测试用例:

        void CMyClassTester::Add_int_int()

        {

        //第一个测试用例

        CaseBegin(); { //1

        int i = 0; //2

        int j = 0; //3

        int ret = pObj->Add(i, j); //4

        ASSERT(ret == 0); //5

        }CaseEnd(); //6

        }

      第1和第6行建立和销毁被测试对象,所加的{}是为了让每个测试用例的代码有一个独立的域,以便多个测试用例使用相同的变量名。

       第2和第3行是定义输入数据,第4行是调用被测试函数,这些容易理解,不作进一步解释。第5行是预期输出,它的特点是当实际输出与预期输出不同时自动报 错,ASSERT是VC的断言宏,也可以使用其他类似功能的宏,使用测试工具进行单元测试时,可以使用该工具定义的断言宏。

      示例 中的格式显得很不简洁,2、3、4、5行可以合写为一行:ASSERT(pObj->Add(0, 0) == 0); 但这种不简洁的格式却是老纳极力推荐的,因为它一目了然,易于建立多个测试用例,并且具有很好的适应性,同时,也是极佳的代码文档,总之,老纳建议:输入 数据和预期输出要自成一块。

      建立了第一个测试用例后,应编译并运行测试,以排除语法错误,然后,使用拷贝/修改的办法建立其他测试用例。由于各个测试用例之间的差别往往很小,通常只需修改一两个数据,拷贝/修改是建立多个测试用例的最快捷办法。

      三 测试用例

       下面说说测试用例、输入数据及预期输出。输入数据是测试用例的核心,老纳对输入数据的定义是:被测试函数所读取的外部数据及这些数据的初始值。外部数据 是对于被测试函数来说的,实际上就是除了局部变量以外的其他数据,老纳把这些数据分为几类:参数、成员变量、全局变量、IO媒体。IO媒体是指文件、数据 库或其他储存或传输数据的媒体,例如,被测试函数要从文件或数据库读取数据,那么,文件或数据库中的原始数据也属于输入数据。一个函数无论多复杂,都无非 是对这几类数据的读取、计算和写入。预期输出是指:返回值及被测试函数所写入的外部数据的结果值。返回值就不用说了,被测试函数进行了写操作的参数(输出 参数)、成员变量、全局变量、IO媒体,它们的预期的结果值都是预期输出。一个测试用例,就是设定输入数据,运行被测试函数,然后判断实际输出是否符合预 期。下面举一个与成员变量有关的例子:

      产品函数:

        void CMyClass::Grow(int years)

        {

        mAge += years;

      if(mAge < 10)

        mPhase = "儿童";

        else if(mAge <20)

        mPhase = "少年";

        else if(mAge <45)

        mPhase = "青年";

        else if(mAge <60)

        mPhase = "中年";

        else

        mPhase = "老年";

        }

      测试函数中的一个测试用例:

        CaseBegin(); {

        int years = 1;

        pObj->mAge = 8;

        pObj->Grow(years);

        ASSERT( pObj->mAge == 9 );

        ASSERT( pObj->mPhase == "儿童" );

        }CaseEnd();

       在输入数据中对被测试类的成员变量mAge进行赋值,在预期输出中断言成员变量的值。现在可以看到老纳所推荐的格式的好处了吧,这种格式可以适应很复杂 的测试。在输入数据部分还可以调用其他成员函数,例如:执行被测试函数前可能需要读取文件中的数据保存到成员变量,或需要连接数据库,老纳把这些操作称为 初始化操作。例如,上例中 ASSERT( ...)之前可以加pObj->OpenFile(); 。为了访问私有成员,可以将测试类定义为产品类的友元类。例如,定义一个宏:

        #define UNIT_TEST(cls) friend class cls##Tester;

      然后在产品类声明中加一行代码:UNIT_TEST(ClassName)。

       下面谈谈测试用例设计。前面已经说了,测试用例的核心是输入数据。预期输出是依据输入数据和程序功能来确定的,也就是说,对于某一程序,输入数据确定 了,预期输出也就可以确定了,至于生成/销毁被测试对象和运行测试的语句,是所有测试用例都大同小异的,因此,我们讨论测试用例时,只讨论输入数据。

       前面说过,输入数据包括四类:参数、成员变量、全局变量、IO媒体,这四类数据中,只要所测试的程序需要执行读操作的,就要设定其初始值,其中,前两类 比较常用,后两类较少用。显然,把输入数据的所有可能取值都进行测试,是不可能也是无意义的,我们应该用一定的规则选择有代表性的数据作为输入数据,主要 有三种:正常输入,边界输入,非法输入,每种输入还可以分类,也就是平常说的等价类法,每类取一个数据作为输入数据,如果测试通过,可以肯定同类的其他输 入也是可以通过的。下面举例说明:

      正常输入

      例如字符串的Trim函数,功能是将字符串前后的空格去除,那么正常的输入可以有四类:前面有空格;后面有空格;前后均有空格;前后均无空格。

      边界输入

      上例中空字符串可以看作是边界输入。

      再如一个表示年龄的参数,它的有效范围是0-100,那么边界输入有两个:0和100。

      非法输入

      非法输入是正常取值范围以外的数据,或使代码不能完成正常功能的输入,如上例中表示年龄的参数,小于0或大于100都是非法输入,再如一个进行文件操作的函数,非法输入有这么几类:文件不存在;目录不存在;文件正在被其他程序打开;权限错误。

       如果函数使用了外部数据,则正常输入是肯定会有的,而边界输入和非法输入不是所有函数都有。一般情况下,即使没有设计文档,考虑以上三种输入也可以找出 函数的基本功能点。实际上,单元测试与代码编写是“一体两面”的关系,编码时对上述三种输入都是必须考虑的,否则代码的健壮性就会成问题。

      四 白盒覆盖    

       上面所说的测试数据都是针对程序的功能来设计的,就是所谓的黑盒测试。单元测试还需要从另一个角度来设计测试数据,即针对程序的逻辑结构来设计测试用 例,就是所谓的白盒测试。在老纳看来,如果黑盒测试是足够充分的,那么白盒测试就没有必要,可惜“足够充分”只是一种理想状态,例如:真的是所有功能点都 测试了吗?程序的功能点是人为的定义,常常是不全面的;各个输入数据之间,有些组合可能会产生问题,怎样保证这些组合都经过了测试?难于衡量测试的完整性 是黑盒测试的主要缺陷,而白盒测试恰恰具有易于衡量测试完整性的优点,两者之间具有极好的互补性,例如:完成功能测试后统计语句覆盖率,如果语句覆盖未完 成,很可能是未覆盖的语句所对应的功能点未测试。

      白盒测试针对程序的逻辑结构设计测试用例,用逻辑覆盖率来衡量测试的完整性。逻 辑单位主要有:语句、分支、条件、条件值、条件值组合,路径。语句覆盖就是覆盖所有的语句,其他类推。另外还有一种判定条件覆盖,其实是分支覆盖与条件覆 盖的组合,在此不作讨论。跟条件有关的覆盖就有三种,解释一下:条件覆盖是指覆盖所有的条件表达式,即所有的条件表达式都至少计算一次,不考虑计算结果; 条件值覆盖是指覆盖条件的所有可能取值,即每个条件的取真值和取假值都要至少计算一次;条件值组合覆盖是指覆盖所有条件取值的所有可能组合。老纳做过一些 粗浅的研究,发现与条件直接有关的错误主要是逻辑操作符错误,例如:  写成&&,漏了写!什么的,采用分支覆盖与条件覆盖的组合,基本 上可以发现这些错误,另一方面,条件值覆盖与条件值组合覆盖往往需要大量的测试用例,因此,在老纳看来,条件值覆盖和条件值组合覆盖的效费比偏低。老纳认 为效费比较高且完整性也足够的测试要求是这样的:完成功能测试,完成语句覆盖、条件覆盖、分支覆盖、路径覆盖。做过单元测试的朋友恐怕会对老纳提出的测试 要求给予一个字的评价:晕!或者两个字的评价:狂晕!因为这似乎是不可能的要求,要达到这种测试完整性,其测试成本是不可想象的,不过,出家人不打逛语, 老纳之所以提出这种测试要求,是因为利用一些工具,可以在较低的成本下达到这种测试要求,后面将会作进一步介绍。

      关于白盒测试用 例的设计,程序测试领域的书籍一般都有讲述,普通方法是画出程序的逻辑结构图如程序流程图或控制流图,根据逻辑结构图设计测试用例,这些是纯粹的白盒测 试,不是老纳想推荐的方式。老纳所推荐的方法是:先完成黑盒测试,然后统计白盒覆盖率,针对未覆盖的逻辑单位设计测试用例覆盖它,例如,先检查是否有语句 未覆盖,有的话设计测试用例覆盖它,然后用同样方法完成条件覆盖、分支覆盖和路径覆盖,这样的话,既检验了黑盒测试的完整性,又避免了重复的工作,用较少 的时间成本达到非常高的测试完整性。不过,这些工作可不是手工能完成的,必须借助于工具,后面会介绍可以完成这些工作的测试工具。

      五 单元测试工具

      现在开始介绍单元测试工具,老纳只介绍三种,都是用于C++语言的。

       首先是CppUnit,这是C++单元测试工具的鼻祖,免费的开源的单元测试框架。由于已有一众高人写了不少关于CppUnit的很好的文章,老纳就不 现丑了,想了解CppUnit的朋友,建议读一下Cpluser 所作的《CppUnit测试框架入门。该文也提供了CppUnit的下载地址。

       然后介绍C++Test,这是Parasoft公司的产品。[C++Test是一个功能强大的自动化C/C++单元级测试工具,可以自动测试任何C /C++函数、类,自动生成测试用例、测试驱动函数或桩函数,在自动化的环境下极其容易快速的将单元级的测试覆盖率达到100%]。[]内的文字引自 http://www.superst.com.cn/softwares_testing_c_cpptest.htm ,这是华唐公司的网页。老纳想写些介绍C++Test的文字,但发现无法超越华唐公司的网页上的介绍,所以也就省点事了,想了解C++Test的朋友,建 议访问该公司的网站。华唐公司代理C++Test,想要购买或索取报价、试用版都可以找他们。老纳帮华唐公司做广告,不知道会不会得点什么好处?

       最后介绍Visual Unit,简称VU,这是国产的单元测试工具,据说申请了多项专利,拥有一批创新的技术,不过老纳只关心是不是有用和好用。[自动生成测试代码 快速建立功能测试用例 程序行为一目了然 极高的测试完整性 高效完成白盒覆盖 快速排错 高效调试 详尽的测试报告]。[]内的文字是VU开发商的网页上摘录的。前面所述测试要求:完成功能测试,完成语句覆盖、条件覆盖、分支覆盖、路径覆盖,用VU可以 轻松实现,还有一点值得一提:使用VU还能提高编码的效率,总体来说,在完成单元测试的同时,编码调试的时间还能大幅度缩短。算了,不想再讲了,老纳显摆 理论、介绍经验还是有兴趣的,因为可以满足老纳好为人师的虚荣心,但介绍工具就觉得索然无味了,毕竟工具好不好用,合不合用,要试过才知道,还是自己去开 发商的网站看吧,可以下载演示版,还有演示课件。

  • 利用 Eclipse 进行单元测试

    2009-10-13 23:41:46

    您的传统代码是不是要求使用匹配的类测试套件才能针对其源代码库运行?针对此类目的,jMock 堪称是一个优秀的测试框架。但是,并不是所有情况都能够适用,尤其是必须以 jMock 不期望的方式构造对象时。为避免生成自定义模拟对象套件才能支持应用程序中的单元测试的麻烦,可以调整 RMock,与 jMock 无缝地结合使用,从而解决这一问题。

    模拟对象将模仿出于指导代码执行的惟一目的而编写的类的行为,以便它在测试时符合代码执行要求。最终,模拟对象数目可以随着应用程序类数目的增长而增长。 使用 jMock、RMock 甚至 EasyMock 等框架有助于消除对物理的独立存在的模拟对象集的需求。

    EasyMock 框架的一个主要缺点是不能模拟具体类 —— 而只能模拟接口。在本文中,我将向您展示怎样使用 jMock 框架来模拟具体类和 接口,以及如何用 RMock 测试某些模糊的情况。

    在 Eclipse IDE 中配置 jMock 和 RMock

    首先启动 Eclipse 集成开发环境 (IDE)。接下来,创建一个基本 Java™ 项目,稍后将把 JUnit、jMock 和 RMock Java Archive (JAR) 库导入到该项目中。将 Java 项目命名为 TestingExample。在 Java Perspective 内,选择 Project > Properties,然后单击 Libraries 选项卡.

    当 JAR 文件位于 Java 类路径(即,已在 Eclipse 内配置的 Java 运行时环境(Java Runtime Environment,JRE))中时,请使用 Add JARs 按钮。Add Variable 按钮适用于文件系统(本地或远程)中的资源(包括 JAR)所驻留的具体目录,并且通常可以引用此按钮。在必须引用 Eclipse 中默认的那些特定资源或为特定的 Eclipse 工作区环境配置的那些特定资源时,请使用 Add Library 按钮。单击 Add Class Folder,从已经配置为项目一部分的一个现有项目文件夹中添加资源。

    对于本示例,请单击 Add External JARs 并浏览到已下载的 jMock 和 RMock JAR。将其添加到项目中。当显示图 2 中所示的属性窗口时,请单击 OK。

    TestExample 源代码

    对于 TestExample 项目,您将使用来自四个类的源代码:

    ServiceClass.java

    Collaborator.java

    ICollaborator.java

    ServiceClassTest.java

    待测试的类将是 ServiceClass,该类包含了一个方法:runService()。服务方法将获取实现简单接口 ICollaborator 的 Collaborator 对象。具体的 Collaborator 类中实现了一个方法:executeJob()。Collaborator 是必须正确模拟的类。

    第四个类是测试类:ServiceClassTest(实现的性质已经被尽可能地简化)。

  • [论坛] 软件工程师不可不知的10个概念

    2008-07-28 22:37:45

     出色的软件工程师善用设计模式,勤于代码重构,编写单元测试,并对简单有宗教般的追求。除了这些,优秀的软件工程师还要通晓10个概念,这10个概念超越了编程语言与设计模式,软件工程师应当从更广的范围内明白这些道理。

    10. 关系数据 (Relational Databases)
    关系数据库因为在大规模 Web 服务上缺乏可扩充性而颇受微词,然而,关系数据库仍然是近20年来计算机技术中最伟大的成就。关系数据库对处理订单,公司数据方面有着出色的表现。

    关系数据库的核心是以记录表示数据,记录存放在数据库表,数据库使用查询语言(SQL)对数据进行搜索与查询,同时,数据库对各个数据表进行关联。

    数据库的标准化技术(normalization)讲的是使用正确的方式对数据进行分存以降低冗余,并加快存取速度。
     
    9. 安全 (Security)
    随着黑客的崛起与数据敏感性的上升,安全变得非常重要。安全是个广义的概念,涉及验证,授权与信息传输。

    验证是对用户的身份进行检查,如要求用户输入密码。验证通常需要结合 SSL (secure socket layer)进行;授权在公司业务系统中非常重要,尤其是一些工作流系统。最近开发的 OAuth 协议可以帮助 Web 服务将相应信息向相应用户开放。Flickr 便使用这种方式管理私人照片和数据的访问权限。

    另外一个安全领域是网络设防,这关系到操作系统,配置与监控。不仅网络危险重重,任何软件都是。Firefox 被称为最安全的浏览器,仍然需要频频发布安全补丁。要为你的系统编写安全代码就需要明白各种潜在的问题。
     
    8. 云计算 (Cloud Computing)
    RWW 最近的关于云计算的文章 Reaching For The Sky Through Compute Clouds 讲到了云计算如何改变大规模 Web 应用的发布。大规模的并行,低成本,与快速投入市场。

    并行算法发明以来,首先迎来的是网格计算,网格计算是借助空闲的桌面计算机资源进行并行计算。最著名的例子是 Berkley 大学的 SETI@home 计划,该计划使用空闲的 CPU 资源分析太空数据。金融机构也大规模实施网格计算进行风险分析。空闲的资源,加上 J2EE 平台的崛起,迎来了云计算的概念:应用服务虚拟化。就是应用按需运行,并可以随着时间和用户规模而实时改变。

    云计算最生动的例子是 Amazon 的 Web 服务,一组可以通过 API 进行调用的应用,如云服务(EC2),一个用来存储大型媒体文件的数据库(S3),索引服务(SimpleDB),序列服务(SQS)。
Open Toolbar