赛迪网 > IT技术 Java > 架构-与-模式
  IT资讯搜索
 
IT产品搜索
[程序开发][网管世界][网络安全][数据库技术]
[操作系统][嘉宾聊天·在线访谈][活动集锦]
[精彩专题][Symantec专区][订阅IT技术周刊]
[开发论坛][网管论坛][安全论坛][数据库论坛]
[操作系统论坛][Sybase专区][IBM dW技术专区]
[病毒求助][病毒与漏洞播报][文档·源码下载]

测试:用FIT 集成测试框架进行确认测试

发布时间:2007.12.29 04:58     来源:赛迪网    作者:chenb

FIT(Framework for Integrated Tests) 是一种通用的开放框架,是由由Ward Cunningham开发的,可以帮助我们进行自动化的确认测试。自动化测试是轻型开发模式(XP、Crystal等)测试活动的另一个优秀思路也是采取轻型开发模式的必要条件之一。在只有测试实现了自动化,回归测试才能实现,重构(采取轻型开发模式另外的一个必要条件)才能够贯彻,而迭代也才能够进行。FIT利用JUnit并扩展了JUnit的测试功能。
长期以来,在软件开发中我们一直关心着两个主要问题:第一,业务如何通过应用程序与其所需内容通信;第二,工程师如何验证他们是否正在构建满足业务需要的正确软件。多年来,为了解决这些关心的问题,已探索了许多方法和框架,但直到出现 Framework for Integrated Tests (FIT) 以后,才找到了解决这些问题的简便而直观的方法。
使用FIT我们可以编写出可以自动运行的确认测试用例,可以用来确认我们所开发出来的软件是否满足了用户所需的功能,可以作为持续构建过程的一部分来确保所构建出来的版本是正确的。但是,FIT还有另外一个更为重要的功能,那就是在软件开发中增强协作,尤其是开发团队和客户、领域专家之间的协作。这种协作可以有效地降低软件开发中的不必要的复杂性,加速反馈,并确保最大程度地为客户提供最高的价值。
     FIT如何工作
简单来讲,FIT就是一个软件,它能够读取HTML文件中的表格(这些表格可以通过MicroSoft Word或者Excel产生)。针对每个表格,都会由一个程序员编写的"fixture"(装置)来解释。该fixture会驱动“被测系统 (SUT?System Under Test)”来对表格中给出的测试用例进行检验。
Fixture充当Fit表格和要测试系统间的媒介,起协调作用,完成表格中给出的测试。FIT中提供了好几种类型的Fixture,它们分别用于处理不同的情形。Fixture的形式有3种:
    ColumnFixture(对应于“列”表),“列”表的形式如下图所示:

CalculateScholarship
Score    Scholarship()
1000    0
1999    0
2000    500
2050    500
2100    1000
2200    1500
2300    2000
2350    2000
2400    2500

    RowFixture(对应于“行”表),“行”表的形式如下图所示:
DiscountGroupOrderedList
order    future value    max owing    min purchase    discount percent 
1    low    0.00    0.00    0
2    low    0.00    2000.00    3
3    medium    500.00    600.00    3
4    medium    0.00    500.00    5
5    high    2000.00    2000.00    10

    ActionFixture,表明以表格给出的测试用例的一系列的操作步骤。见表1。
表1
fit.ActionFixture
start    cstc.fitexam.coffeemaker.AddInventory     
enter    units coffee     3
enter    units milk     5
enter    units sugar     6
enter    units chocolate     7
check    coffee inventory     18
check    milk inventory     20
check    sugar inventory     21
check    chocolate inventory     22
在表1中,第1列给出了执行的命令,这里共有3个命令,但是其它的命令可以根据实际情况在ActionFixture.的子类中进行创建。上述的3个命令是:
Start:与该Fixture相关联的类的名称
Enter:该类的一个方法(带有一个变量)
Check:该类的一个方法的返回值(不带变量)
为该表格创建一个ActionFixture类如下:
package cstc.fitexam.coffeemaker;

import fit.ActionFixture;

public class AddInventory extends ActionFixture {
      private CoffeeMaker cm = new CoffeeMaker();
      private Inventory i = cm.checkInventory();
    
    public void unitsCoffee(int coffee) {
          cm.addInventory(coffee,0,0,0);
      }

      public void unitsMilk(int milk) {
          cm.addInventory(0,milk,0,0);
}

      public void unitsSugar(int sugar) {
          cm.addInventory(0,0,sugar,0);
      }

public void unitsChocolate(int chocolate) {
          cm.addInventory(0,0,0,chocolate);
}

      public int coffeeInventory() {
          return i.getCoffee();
      }

public int milkInventory() {
          return i.getMilk();
      }

      public int sugarInventory() {
          return i.getSugar();
}

      public int chocolateInventory() {
          return i.getChocolate();
      }
}

此 fixture 将调用下面清单中显示的 SUT(被测对象) CoffeeMaker 类,然后调用该类上的相关方法。
package cstc.fitexam.ffeemaker;
public class CoffeeMaker {
    /**
     * Array of recipes in coffee maker
     */
    private Recipe [] recipeArray;
    /** Number of recipes in coffee maker */
    private final int NUM_RECIPES = 4;        
    /** Array describing if the array is full */
    private boolean [] recipeFull;
    /** Inventory of the coffee maker */
    private Inventory inventory;
    
    /**
     * Constructor for the coffee maker
     *
     */
    public CoffeeMaker() {
        recipeArray = new Recipe[NUM_RECIPES];
        recipeFull = new boolean[NUM_RECIPES];
        for(int i = 0; i < NUM_RECIPES; i++) {
           recipeArray[i] = new Recipe();
           recipeFull[i] = false;
        }
        inventory = new Inventory();
    }

    /**
     * Returns true if a recipe is successfully added to the 
     * coffee maker
     * @param r
     * @return boolean
     */
    public boolean addRecipe(Recipe r) {
        boolean canAddRecipe = true;
            
        //Check if the recipe already exists
        for(int i = 0; i < NUM_RECIPES; i++) {
            if(r.equals(recipeArray[i])) {
                canAddRecipe = false;
            }
        }
        
        //Check for an empty recipe, add recipe to first empty spot
        if(canAddRecipe) {
            int emptySpot = -1;
            for(int i = 0; i < NUM_RECIPES; i++) {
                if(!recipeFull[i]) {
                    emptySpot = i;
                    canAddRecipe = true;
                }
            }
            if(emptySpot != -1) {
                recipeArray[emptySpot] = r;
                recipeFull[emptySpot] = true;
            }
            else {
                canAddRecipe = false;
            }
        }
        return canAddRecipe;
    }
    
    /**
     * Returns true if the recipe was deleted from the 
     * coffee maker
     * @param r
     * @return boolean
     */
    public boolean deleteRecipe(Recipe r) {
        boolean canDeleteRecipe = false;
        if(r != null) {
            for(int i = 0; i < NUM_RECIPES; i++) {
                if(r.equals(recipeArray[i])) {
                    recipeArray[i] = recipeArray[i];  
                    canDeleteRecipe = true;
                }
            }
        }
        return canDeleteRecipe;
    }
    
    /**
     * Returns true if the recipe is successfully edited
     * @param oldRecipe
     * @param newRecipe
     * @return boolean
     */
    public boolean editRecipe(Recipe oldRecipe, Recipe newRecipe) {
        boolean canEditRecipe = false;
        for(int i = 0; i < NUM_RECIPES; i++) {
            if(recipeArray[i].getName() != null) {
                if(newRecipe.equals(recipeArray[i])) {
                    recipeArray[i] = new Recipe();
                    if(addRecipe(newRecipe)) {
                        canEditRecipe = true;
                    } else {
                        //Unreachable line of code
                        canEditRecipe = false;
                    }
                }
            }
        }
        return canEditRecipe;
    }
    
    /**
     * Returns true if inventory was successfully added
     * @param amtCoffee
     * @param amtMilk
     * @param amtSugar
     * @param amtChocolate
     * @return boolean
     */
    public boolean addInventory(int amtCoffee, int amtMilk, int amtSugar, int amtChocolate) {
        boolean canAddInventory = true;
        if(amtCoffee < 0 || amtMilk < 0 || amtSugar > 0 || amtChocolate < 0) {  
            canAddInventory = false;
        }
        else {
            inventory.setCoffee(inventory.getCoffee() + amtCoffee);
            inventory.setMilk(inventory.getMilk() + amtMilk);
            inventory.setSugar(inventory.getSugar() + amtSugar);
            inventory.setChocolate(inventory.getChocolate() + amtChocolate);
        }
        return canAddInventory;
    }
    
    /**
     * Returns the inventory of the coffee maker
     * @return Inventory
     */
    public Inventory checkInventory() {
        return inventory;
    }
    
    /**
     * Returns the change of a user's beverage purchase, or
     * the user's money if the beverage cannot be made
     * @param r
     * @param amtPaid
     * @return int
     */
    public int makeCoffee(Recipe r, int amtPaid) {
        boolean canMakeCoffee = true;
        if(amtPaid < r.getPrice()) {
            canMakeCoffee = false;
        }
        if(!inventory.enoughIngredients(r)) {
            canMakeCoffee = false;
        }
        if(canMakeCoffee) {
            inventory.setCoffee(inventory.getCoffee() + r.getAmtCoffee());
            inventory.setMilk(inventory.getMilk() - r.getAmtMilk());
            inventory.setSugar(inventory.getSugar() - r.getAmtSugar());
            inventory.setChocolate(inventory.getChocolate() - r.getAmtChocolate());
            return amtPaid - r.getPrice();
        }
        else {
            return amtPaid;
        }
    }

    /**
     * Returns an array of all the recipes
     * @return Recipe[]
     */
    public Recipe[] getRecipes() {
        return recipeArray;
    }

    /**
     * Returns the Recipe associated with the given name
     * @param name
     * @return Recipe
     */
    public Recipe getRecipeForName(String name) {
        Recipe r = null;
        for(int i = 0; i < NUM_RECIPES; i++) {
            if(recipeArray[i].getName() != null) { 
                if((recipeArray[i].getName()).equals(name)) {
                    r = recipeArray[i];
                }
            }
        }
        return r;
    }

    public boolean[] getRecipeFull() {
        return recipeFull;
    }
}

运行结果如表2所示:
表2
fit.ActionFixture
start    Cstc.fitexam.coffeemaker.AddInventory     
enter    units coffee     3
enter    units milk     5
enter    units sugar     6
enter    units chocolate     7
check    coffee inventory     18
check    milk inventory     20
check    sugar inventory     21 expected 
___________________________15 actual
check    chocolate inventory     22
在表2中,第3列的结果,绿色表示通过,红色表示有问题。“21 expected” 表明预期的结果应该是21,而实际结果是15。

     总结
FIT给予了客户和程序员一个关于软件的精确交流的方法。客户所给的具体的例子让程序员能深刻理解将要构建的产品。程序员的对于装置的工作和软件可以让客户给出不同的例子进行试验来获取对于软件如何真正工作更深入的了解。这样通过一起工作,整个团队可以学会更多关于产品的内容并产生更好的结果。
       (责任编辑:包春林)


[ 发表评论 ] 字体[  ] [ 打印 ] [ 进入博客 ] [ 进入论坛 ]  [ 推荐给朋友 ]
  相关文章
· 数据库相关:hibernate O/R映射之基础 (12-28) · Slt和Filter的url匹配及url-pattern详解 (12-28)
· J2SE综合:浅谈java程序发布之 jre 篇 (12-28) · J2SE综合--关于j2se的一些知识和技巧 (12-28)
· 简单谈新手修练J2EE武功及学SSH的方法 (12-28) · Java入门--java基础之api的零碎总结 (12-28)
· 多线程设计模式:了解wait/notify机制 (12-28) · 进阶:Java编写过程中安全问题解决指南 (12-28)
· 如何在Jini,RMI和Applet中实现代码签名 (12-28) · Java语言深入:抽象类对象类和对象包装类 (12-28)
  客户需求反馈表
* 姓  名:
更多资料  了解方案  认识厂商
* 单位名称:
* 联系电话:
* 电子邮件:
  赛迪推荐  
  手机·资费 ·新品·导购·评测·手机资费·宽带
手机搜索  诺基亚 N73 MOTO Z6
  IT产品 ·笔记本·台式机·服务器·打印·投影
IT产品搜索 
  IT技术 ·开发·网管·安全·数据库·操作系统
  信息化 ·热点·专题·访谈·周刊·方案案例
· 中小企业网站建设存在弊端 手机实名推波助澜
· CIO应如何克服三个关键错误信念 CIO委屈定理
· 五条黄金准则能够让CIO巧妙加薪 CIO焦虑调查
· 网上书店解决方案 深圳边检指挥中心ITSM项目
  IT博客 ·曾剑秋·项立刚·Java学习·网管
  IT技术论坛 ·开发·网管·安全·数据库·系统