<th id="v9g6b"><track id="v9g6b"></track></th>

  • <em id="v9g6b"><acronym id="v9g6b"></acronym></em>
    <progress id="v9g6b"><pre id="v9g6b"></pre></progress>
    <tbody id="v9g6b"></tbody>
    <button id="v9g6b"><acronym id="v9g6b"></acronym></button>
    <rp id="v9g6b"><ruby id="v9g6b"></ruby></rp>

        <dd id="v9g6b"></dd>

        <em id="v9g6b"></em>

          B/S开发框架测试驱动开发(TDD)指导书(二)

          本指导书详细介绍了敏捷实践——TDD的各个方面。TDD是提升单元测试有效性的工程方法,Web开发框架中遵循测试先行、小步快跑和及时重构的原则,能有效减少过度设计,促进降低开发成本、以及提高软件质量和设计效率。另外,本指导书还对遗留系统的TDD,以及TDD工具的选择给予参考性建议,B/S开发框架并通过大量的实践案例引导你充分体验TDD的魅力。

          怎么做TDD

          1.1  一个TDD的例子

          B/S开发框架TDD例子需求描述:开发一个堆栈的数据结构,该堆栈的容量是10,存储正整数。请实现堆栈的基本操作,包括PUSH、POP、TOP、IsEmpty:

          PUSH:压栈操作,向堆栈的顶部添加一个数据;

          POP:出栈操作,删除堆栈顶部的数据,并将其值返回;

          TOP:返回堆栈顶部的数据,但不删除;

          IsEmpty:判断该堆栈是否为空?返回一个布尔值。

          准备工作:

          1)        创建Stack源文件夹和空文件,其中stack.c为实现,stack.h为提供的外部接口;

          2)        创建Stack源文件的测试文件夹及所需文件,tested_src_file.h中仅include "stack.h";

          B/S开发框架做TDD怎么做

          3)        B/S开发框架创建测试用例集文件,以被测函数为单位,例:Test_Stack_IsEmpty.h;

          4)        测试用例集文件格式如下:

          #include "tested_src_file.h"

          #include<cxxtest/TestSuite.h> classTest_Stack_IsEmpty :publicCxxTest::TestSuite { private: public: voidsetUp() { GlobalMockObject::reset(); } voidtest_xxx1() { … } voidtest_xxx2() { … } voidtearDown() { GlobalMockObject::verify(); } };

          每个测试用例执行前执行一次

          每个测试用例执行后执行一次

          测试用例1,一定要以test打头,返回void

          测试用例2

          一、用例一

          1、Web开发框架TDD应用新增测试用例

          1)        B/S开发框架分析需求,考虑测试用例,尽量黑盒化

          对于IsEmpty,主要有两个场景,一个是当堆栈为空时返回true,另一个是当堆栈不为空时返回false;

          2)        B/S开发框架根据场景增加用例,增加当堆栈为空时返回true的用例。

          class Test_Stack_IsEmpty : public CxxTest::TestSuite
          {
          private:
          public:
          …新增
          void test_should_return_true_if_stack_is_empty() 
          {
          STACK_STRU stack;
          TS_ASSERT_EQUALS(STACK_IsEmpty(&stack), true);
          }
          …
          };

          B/S开发框架运行测试,此时由于没有实现,所以一定会失败,有编译错误。
          2、运行测试失败

          新增


          $ make clean all
          compiling test_stack.cpp ...
          test_stack.cpp
          ?-\app\src\stack\stack.c(8) : error C2065: ?°STACK_STRU?±:未声明的标识符
          ?-\app\src\stack\stack.c(8) : error C2146:语法错误:缺少?°)?±(在标识符?°stack?±的前面)
          ?-\app\src\stack\stack.c(8) : error C2059:语法错误: ?°)?±
          ?-

          3、写部分代码

          typedef struct {
          }STACK_STRU;
          
          Stack.h
          bool STACK_IsEmpty(STACK_STRU *pStack)
          {
               return true;
          }
          
          Stack.c
          bool STACK_IsEmpty(STACK_STRU *pStack)
          {
              return true;
          }


          你一定会质疑,这里就没有实现嘛,在不熟悉的时候可以使用伪实现,等WEB开发框架测试用例足够后,再逼迫你真正实现它,有种逐渐清晰的感觉,对功能非常熟悉的人可以真正实现。

          4、运行测试通过

          $ make clean all
          compiling test_stack.cpp ...
          test_stack.cpp
          Linking test_stack.exe ...
          Running 1 test.OK!
          rm test_stack.cpp


          5、重构

          此时未有重复或不清晰的代码,无需重构,继续下个用例。

          二、用例二

          1、新增测试用例

          根据场景增加用例,增加当堆栈不为空时返回false的用例,发现空的Stack不足以满足测试要求,先修改之前的测试。


          class Test_Stack_IsEmpty: public CxxTest::TestSuite
          {
          private:
          public:
          …
          voidtest_should_return_true_if_stack_is_empty()
          {
          STACK_STRU stack;
          stack.elementNum = 0;
          TS_ASSERT_EQUALS(STACK_IsEmpty(&stack), true);
          }
          …
          };

          再在Stack结构中增加成员。


          Stack.h

          typedef struct
          {
              int elementNum;
          }STACK_STRU;

          根据场景增加用例,增加当堆栈不为空时返回false的用例。

          class Test_Stack_IsEmpty : public CxxTest::TestSuite
          {
          private:
          public:
          …
          voidtest_should_return_true_if_stack_is_empty()
          {
          STACK_STRU stack;
          stack.elementNum = 0;
          TS_ASSERT_EQUALS(STACK_IsEmpty(&stack), true);
          }
          
          void test_should_return_false_if_stack_is_not_empty()
          {
          STACK_STRU stack;
          stack.elementNum = 1;
          TS_ASSERT_EQUALS(STACK_IsEmpty(&stack), false);
          }
          …
          };

          注:这里对堆栈状态的测试优于对堆栈操作行为的测试,当然这个状态也可以由函数方法返回。

          2、运行测试失败

          由于前面没有真正实现STACK_IsEmpty,所以一定会失败。

          $ make clean all
          compiling test_stack.cpp ...
          test_stack.cpp
          Linking test_stack.exe ...
          Running 2 tests.
          In Test_Stack_IsEmpty::test_should_return_false_if_stack_is_not_empty:
          ?-\app\ut\src\stack\Test_Stack_IsEmpty.h:28: Error: Expected (STACK_IsEmpty(&stack) == false), found (true != false)
          Failed 1 of 2 tests
          Success rate: 50%
          make: *** [test_stack.exe] Error 1
          rm test_stack.cpp

          3、写部分代码

          Stack.c


          bool STACK_IsEmpty(STACK_STRU *pStack)
          {
              return (pStack->elementNum == 0);
          }
          

          4、运行测试通过

          $ make clean all
          compiling test_stack.cpp ...
          test_stack.cpp
          Linking test_stack.exe ...
          Running 2 tests..OK!
          rm test_stack.cpp

          5、重构

          考虑到stack在两个用例中都有,为了消除重复,提取到测试用例集的private段中,在WEB开发框架重构后,需要运行测试,等测试通过后,再继续下一个用例。

          class Test_Stack_IsEmpty : public CxxTest::TestSuite
          {
          private:
          
          STACK_STRU stack;
          public:
          …
          void test_should_return_true_if_stack_is_empty()
          {
          stack.elementNum = 0;
          TS_ASSERT_EQUALS(STACK_IsEmpty(&stack), true);
          }
          
          voidtest_should_return_false_if_stack_is_not_empty()
          {
          stack.elementNum =1;
          TS_ASSERT_EQUALS(STACK_IsEmpty(&stack),false);
          }
          …
          };

          遗留问题:Stack的结构定义对测试可见,造成了测试和代码的耦合,怎么消除呢?

          三、用例N

          Web开发框架代码重构——当完成Top、Pop操作后,发现这两个操作在取栈顶元素时,完全相同,重构Pop;

          web开发框架用例重构前后对比

          注:此时的被测单元,已经不单是被测函数,还包含其调用的内部函数(相对Stack.c而言),重构时如果影响测试,则需要先修改测试,万事以测试为先。

          测试重构——当完成Top、Pop等操作后,发现这些操作的测试都用到非空的栈,用Push就可以不访问Stack内部元素,尽可能作为黑盒来测试。

          web开发框架用例重构前后对比

          注:有人提出“能否先实现Push,再实现Top/Pop?”,并不反对这样做,但这样会导致代码内的实现依赖,建议在重构时考虑,不论如何,我们的目标是减少耦合(含代码内部、测试代码内部、测试和代码间)。

          1.2  产品开发TDD步骤

          一般TDD的书籍或者培训材料,介绍的都是比较独立的被测代码,对于这样的代码,很容易开展TDD。但在产品开发中,我们需要测试的是一个复杂系统中的代码,这些代码跟周边模块有很多耦合和交互,要开展TDD,会有很多困难,不那么容易。下面将阐述,对于一个产品开发中的模块,如何开展TDD。

          l  选择TDD工具。后文将详细阐述选择工具需要考虑哪些因素,请参见后文。

          l  确定合适的被测对象。我们不应该针对整个系统做TDD,因为整个系统的行为太复杂了,很难通过测试用例描述。我们也不应该针对单个函数做TDD,因为系统中的大部分函数都不是实现一个独立的功能,边界不明确。如何选择被测对象呢?一方面,被测对象必须是内聚的,功能明确的;另一方面,被测对象需要在一个可以控制的粒度。一般建议以一个模块或者子模块作为被测对象。

          l  搭建模拟产品的测试环境。首先建立一个测试工程,把被测文件加进来。被测对象跟周边有很多接口,需要模拟这些周边接口。周边接口可以简单打桩,但一般而言,为了使周边接口更加逼真一些,可以构造一些数据,基于数据实现这些接口。这样构造测试条件,判断测试结果的时候,可以围绕这些数据展开,非常容易。

          l  合入持续集成环境。TDD测试工程和测试代码,必须跟代码配置库放在一起,持续维护。需要在持续集成环境中执行TDD测试,这样一旦代码被破坏,可以立即发现并修复。

          l  正式开展TDD。已经具备TDD测试环境,接下来就可以按照TDD的标准动作开展TDD了。在编写测试用例的过程中,被测代码可能会对周边模块有新的依赖,可以根据需要继续补充对周边环境的模拟。

          1.3  什么时候做TDD

          TDD作为敏捷的一个实践,往往是和迭代开发相关联的,那么TDD应该在整个迭代Story实施过程中的什么时候开展呢?

          在一个完整的Story实施过程中,Story需要进行需求澄清,方案设计,开发,编写验收用例,编写自动化脚本,进行验收测试等,这些环节没有明确的界限,不需要严格区分阶段。其中需求澄清需要贯穿整个Story的实施过程,Story设计主要指方案层面的设计,Story开发指代码层面的设计、编码和开发者测试等。TDD则主要用于Story开发环节。

          B/S开发框架TDD在Story中的开发环节实施

                                                                                                    图1TDD在Story实施过程中的位置

          Story实现的是一个完整的需求,很可能贯穿系统中的很多模块。TDD的被测对象是系统中的一个模块或者子模块,不是Story。虽然强调TDD用例必须基于功能测试,但测试的是某一个模块代码的功能,而不是整个Story的功能。整个Story的功能通过Story验收用例保证。

          如果想在传统项目中开展TDD,可以把详细设计、编码、UT等活动融合在一起,采用TDD的方式开发。如果模块不是很大,IT不是很明显,也可以融合到TDD活动中。

          1.4  谁负责做TDD

          前面提到,TDD用于Story开发环节,包括代码层面的设计,编码,开发者测试等。因此,TDD是开发者测试,而不是验收测试。既然是开发者测试,应该由开发人员负责做TDD。

          我们现在提倡测试前移和开发测试融合,既然这样,开发人员和测试人员就没有明确的界限。测试人员在如何构造测试用例方面具备优势,我们需要充分发挥测试人员的优势,取长补短。测试人员可以参与TDD,开发人员也可以编写自动化测试脚本。

          1.5  测试对象的选择

          B/S开发框架TDD主要指单元测试。单元测试是指单个函数或几个函数的测试吗?不!测试的目的是保证被测代码能够实现我们需要的功能,而不应该关心代码怎么实现。如果被测代码是一个函数,则测试的是函数的功能;如果被测代码是一个文件或者一个类,则测试的是这个文件或者类的功能;如果被测代码是一个模块,则测试的是这个模块的功能。我们的产品代码,少部分函数提供相对独立的功能,大部分函数是一些流程处理。一般而言,对于功能独立的函数,可以针对函数测试,而其它函数,建议以模块作为被测对象。一方面,模块的功能比较明确,周边接口比较清晰;另一方面,模块不是很大,我们有能力熟悉模块的每一个行为细节,掌控这个模块。

          1.6  测试用例如何写

          如前所述,测试的目的是保证被测代码能够实现我们需要的功能,而不应该关心代码怎么实现,且测试用例要能方便的自动化重复执行。因此,对测试用例有如下要求:

          l  要保证测试用例的完备性和有效性

          用例设计要从用户如何使用的角度出发,覆盖所有使用场景,避免遗漏。测试用例重点关注单元的功能(黑盒测试),这里的单元指共同完成单一功能的函数集。测试用例的描述要简洁,并能够准确表达测试意图以及要解决的问题。除了正常的场景外,测试用例还需要关注到异常的场景,通常需要实现如下几种测试用例:

          1、 Positive test cases (正确的数据,正确的输出)

          2、 Negative test cases (错误或丢失数据,正确的handling)

          3、 Exception test cases (能够正确的抛出和捕获exception)

          l  测试用例的粒度要小

          TDD倾向于采用小步骤(Small Step)开发,通过解决一系列的小问题来实现一个复杂的系统。测试用例越细,对问题的解决就越简单。建议在15分钟内,能够完成编码并使得一个测试执行通过。

          l  测试用例要独立

          Web开发框架每个测试用例尽量保持其独立性(解耦),不应该与其他测试交互。这样就避免因为一个测试的失败而导致很多其他测试失败。相互独立还意味着所有的测试都是不依赖于测试顺序,如果只想挑选一个或部分用例进行测试也是可行的。每项测试对于公共变量、测试环境的更改也不能影响其他测试,某一个测试执行后要把系统恢复到执行前的状态。

          1.7  检查TDD实施结果

          如何对TDD实施结果进行检查,检查标准是什么?下面从几个不同维度给出具体的检查点。

          l 符合需求

          TDD要符合Story需求,TDD虽然要求功能代码必须在测试执行中通过,好像提供了保障,但是测试用例跟Story之间是否一致,还需要人为检查。TDD要从用户使用角度,而不是纯粹地从测试技术角度来验证。因此,要检视测试用例是否符合Story需求。

          l 覆盖率

          用例是否覆盖所有代码行。与传统开发一样,采用覆盖率检查工具,但是略有不同,TDD要求的是由测试用例推出功能代码,不做多余的设计,因此,开发完成后的天然覆盖率的应该是100%,如果覆盖率小于100%,出现未覆盖的代码行,则说明存在多余的设计,或者遗漏测试用例,要想办法去除代码或者补充用例。

          l 自动化

          TDD尽量做到自动化验证,要求测试用例能够被自动化,即每一个测试用例都要对应可以执行的测试代码,测试自动化程度100%。

          l 测试顺序检查

          TDD中的测试用例相互独立,不互相依赖。所以,可以调换测试用例的执行次序,以确认无依赖。如果出现依赖的情况,要想办法消除依赖。每个测试用例,每个测试套,执行完毕以后,需要将被测对象恢复到最初的状态。

          标签: B/S开发框架测试驱动开发(TDD)

          网站&系统开发技术学习交流群:463167176

          本站文章除注明转载外,均为本站原创或翻译,欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,共创和谐网络环境。
          转载请注明:文章转载自:软件开发框架 ? B/S开发框架测试驱动开发(TDD)指导书(二)
          本文标题:B/S开发框架测试驱动开发(TDD)指导书(二)
          本文地址:http://www.sajuice.com/OrgTec/Agile/0008.html

          相关文章: 软件工程UML建模及图表介绍

          电话
          电话 18718672256

          扫一扫
          二维码
          本港台开奖 <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>