[软件测试][敏捷开发下的测试][六][学习笔记]

举报
John2021 发表于 2022/05/16 23:05:17 2022/05/16
【摘要】 1.前言全球市场竞争的日趋激烈和一体化进程,驱使着今天的商业项目不断缩短发布时间,同时还要不断地为客户提供更高质量的产品。遗憾的是老一套的传统开发模式已经不能够适应这种激烈的竞争环境了。在本世纪指出,一批来自各个领域的开发人员开始讨论轻量化和快速的开发方法。创建了著名的《敏捷软件开发宣言》(简称《敏捷宣言》),它成为敏捷运动的基石。我们一直在实践中探寻更好的软件开发方法,身体力行的同时也帮...

1.前言

全球市场竞争的日趋激烈和一体化进程,驱使着今天的商业项目不断缩短发布时间,同时还要不断地为客户提供更高质量的产品。遗憾的是老一套的传统开发模式已经不能够适应这种激烈的竞争环境了。
在本世纪指出,一批来自各个领域的开发人员开始讨论轻量化和快速的开发方法。创建了著名的《敏捷软件开发宣言》(简称《敏捷宣言》),它成为敏捷运动的基石。

我们一直在实践中探寻更好的软件开发方法,身体力行的同时也帮助他人。由此我们建立了如下价值观:
        个体和互动  高于  流程和工具
        工作的软件  高于  详尽的文档
        客户合作    高于  合同谈判
        响应变化    高于  遵循计划
也就是说,尽管右项有其价值,我们更重视左项的价值。
Kent Beck            Mike Beedle      Arie van Bennekum
Alistair Cockburn    Ward Cunningham  Martin Fowler
James Grenning       Jim Highsmith    Andrew Hunt
Ron Jeffries         John Kern        Brian Marick
Robert C. Martin     Setve Mellor     Ken Schwaber
Jeff Sutherland      Dave Thomas

2.敏捷开发的特征

敏捷开发提倡迭代式和增量式的开发模式,并强调测试在其中的重要作用。这是一个围绕以用户为中心,以客户需求为导向的开发过程,在此过程中随时做好迎接变化的准备。
出乎意料的是敏捷开发没有单一固定的开发方法或过程,很多快速的开发模式都可以看做是敏捷。这些模式的确有三个共同点:依赖客户的参与、测试驱动以及紧凑的迭代开发周期。
敏捷开发方法列要

方法 描述
敏捷建模 不是一种建模方法,而是一组建模以及文档化系统的原则和惯例,用以支撑其他诸如极限编程和sScrum等敏捷方法
敏捷统一过程 为敏捷量身定做的统一软件过程(RUP)的精简版
动态系统开发方法 基于快速软件开发方法,依赖于客户的持续参与,使用迭代式和增量式的开发模式,目标是软件能够在预算之内及时交付
核心统一过程(EssUP) 只选择统一软件过程中那些适合当前项目的实践(如用例驱动和团队编程)不管是否需要,RUP通常使用所有实践
极限编程 另一种迭代式和增量式的开发模式,非常强调并依赖单元测试和验收测试,也许是最著名的敏捷方法
功能驱动开发(FDD) 使用工业界的最佳实践,以客户提供的功能需求为驱动,频繁发布小版本,使用领域对象建模以及组建功能团队
开放统一过程 这种敏捷方法实现了标准的统一过程,采纳该方法的软件组能够做到快速开发其产品
Scrum 一种迭代式和增量式的项目管理方法,支持多个敏捷开发模式
进度跟踪 适用所有的敏捷方法,用来度量敏捷开发的速度以及进度

3.敏捷测试

本质上,敏捷测试是协同测试的一种形式,要求每一个人都参与到测试计划的设计、实现以及执行中去。客户通过定义用例以及程序属性参与到定义验收测试的设计中来。开发者和测试者共同打造可以进行功能自动化的测试配件。敏捷测试需要每个人都参与测试过程中,伴随着大量的沟通和协作工作。
敏捷测试需要客户今早参与到开发周期中来并一直到其结束。一旦开发者的代码库稳定之后,客户需要开始验收测试并向开发团队提供反馈。意味着测试不是独立的一个阶段,而是和开发过程紧密联系并驱动开发。
为了保证交付验收测试的是稳定版,开发者通常从创建单元测试开始,然后实现软件单元代码。单元测试是失败验证测试,开发者从破坏的角度设计这些测试用例。
快速软件开发需要及时反馈,敏捷测试依赖于自动化测试。只要保证测试和开发工作在相同的环境和工具下,那么使用什么样的工具并不重要。
敏捷开发环境常常由小型团队构成,这里开发人员也要分饰测试角色。测试者不能仅仅是把问题找出来并交给开发人员修复,他们的任务是通过持续的测试反馈推动项目前行,帮助开发者修复bug、改需求及其他一般性质量提升。
敏捷测试的精神很好地契合了极限编程方法对单元测试先行地需求。

4.极限编程与测试

20世纪90年代出现了一种名为极限编程(eXtreme Programming,XP)的新型软件开发方法。一位名叫Kent Beck的项目经理设计了这种轻量、敏捷的开发过程,并于1996年在戴姆勒-克莱斯勒公司的项目中进行了首次测试并流行至今。
开发XP很可能是为了支持诸如Java、VB、C#等编程语言的应用。这些面向对象语言使得开发人员能够更迅速开发大型复杂应用。伴随着快速应用开发语言带来的好处,不利之处也随之显现。其质量未得到保证,程序经常不满足客户的规格要求和期望,而XP开发方法的目的是短时间内开发高质量的程序。
XP模型除了需要客户参与之外,还高度依赖模块的单元和验收测试。总体来说,对任何一个递增的代码变更,开发人员都必须进行单元测试,以确保代码库满足其规格说明的要求。测试在XP的地位非常重要,所以需要首先创建单元测试和验收测试,然后才能创建代码库。这种形式的测试称为极限测试(eXtreme Testing,XT)。

4.1.极限编程基础

XP是一种可以使开发人员快速生产高质量代码的开发过程,倾向于中小规模的项目。
XP关注点是:

  • 实现简单的设计
  • 开发人员与客户的沟通协作
  • 不断地测试代码库
  • 重构以适应规格说明的变更
  • 寻求用户的反馈

XP与传统开发过程不同之处:

  • 避免了大规模项目的综合征(开始编码之前客户与开发人员设计每个细节,但是需求不断变更)
  • 避免了编写不需要的功能。
  • 将精力集中在测试上

XP开发模型用12个核心实践来驱动该过程。可以归纳为4个概念:

  1. 聆听客户和其他程序员的谈话
  2. 与客户合作,开发应用程序的规格说明和测试用例
  3. 结对编程
  4. 反复测试代码库
实践 注解
1.计划与需求分析 将市场和业务开发人员集中起来,共同确认每个软件特征的最大商业价值;以使用场景的形式重新编写每个重要的软件特征;程序员估计完成每个使用场景的时间;客户根据估计时间和商业价值选择软件的功能特征
2.小规模、递增地发布 努力添加细微、实在、可增值特征,频繁发布新版本
3.系统隐喻 编程小组确认隐喻,便于建立命名规则和程序流程
4.简要设计 实现最简单的设计,使代码通过单元测试。假设变更即将发生,因此不要在设计上花太多时间,只是不停地实现
5.连续测试 在编写模块之前就生成单元测试用例。模块只有在通过单元测试之后才完成。程序只有通过所有单元测试和验收测试后才算完成。
6.重构 清理和调整代码库。单元测试有助于确保在此过程中不破坏程序的功能。应在任何重构后重新进行所有单元测试
7.结对编程 两位程序员协同工作,在同一台机器开发代码库。这样就可以对代码进行实时检查,能极大提高缺陷的发现率和纠正率
8.代码的集体所有权 所有代码归全体程序员所有,没有哪个程序员只致力于开发某一个代码库
9.持续集成 每天在变更通过单元测试之后将其集成到代码库中
10.每周40小时工作 不允许加班,每周都全力工作40小时,就不需要加班。在重大发布前的一星期例外。
11.客户在现场 开发人员和编程小组可以随时接触客户,这样可以快速、准确地解决问题,使开发过程不至于中断。
12.按标准编码 所有代码看上去必须一致。设计一个系统隐喻有助于满足该原则。

4.1.1.XP计划

一个成功的计划阶段将为整个XP过程奠定了基础。XP的计划阶段和传统开发模型不同,通常需求收集与应用设计结合起来。XP中的计划重点是确定客户的应用需求,然后设计使用场景(或用例故事,User Story)来满足客户的应用需求。

4.1.2.XP测试

基于XP的方法取得成功的关键是进行连续的测试。虽然连续测试的原则包含了验收测试,但单元测试占了主要部分。设计单元测试是用来导致程序失败。

4.1.3.XP项目的示例

  1. 程序员与客户会晤,决定产品需求并建立使用场景。
  2. 在客户不在场的情况下,程序员进行会晤,将需求分解为独立的任务,并估计完成每项任务所需的时间。
  3. 程序员向客户提交任务清单和时间估计,并要求客户产生一个功能优先级清单。
  4. 编程小组依据程序员具备的能力,将任务分配给结对的程序员。
  5. 每一对程序员依据应用程序的规格说明,对其编程任务生成单元测试用例。
  6. 每一对程序员完成其任务,皆在编写出通过单元测试的代码库。
  7. 每一对程序员在所有单元测试通过之前,不断修改和重测他们的代码。
  8. 所有的结对程序员每天都整合、集成他们的代码库。
  9. 编程小组发布应用程序的一个预览版本。
  10. 客户进行验收测试,要么确认该应用程序,要么提交一份报告指出存在的bug或不足。
  11. 程序员在验收测试成功的基础上发布一个产品版本。
  12. 程序员根据最新的经验更新时间估计。

4.2.极限测试:概念

为了满足XP的流程和思想,开发人员使用了极限测试方法,该方法强调连续测试。极限测试主要由两种类型的测试组成:单元测试和验收测试。极限测试和传统测试的目标仍然相同:即确定程序中的错误。

4.2.1.极限单元测试

单元测试是极限测试中采用的主要测试方法,具有两条简单规则:所有代码模块在编码之前必须设计好单元测试用例,在产品发布之前必须通过单元测试。极限测试中的单元测试与前面描述的单元测试最大区别在于,极限测试中的单元测试必须在模块编码之前就完成设计和生成。
在开始编码之前设计单元测试所带来的一些好处:

  • 获得了代码将满足其规格说明的信心。
  • 在开始编码之前,就展现了代码的最终结果。
  • 更好地理解应用程序的规格说明和需求。
  • 可以先实现一些简单的设计,稍后再放心地重构代码以改善程序的性能,而无须担心破坏应用程序地规格说明。

首先设计单元测试地做法就是XP方法的闪光点,因为可以迫使我们在开始编码之前,首先理解规格说明,排除了混淆。
常用的面向对象编程语言中,模块常常就是类,甚至是单个的类方法。通常要用自动化测试工具来减轻连续执行单元测试的负担。测试工具还可以生成报告,并对缺陷进行分类。

4.2.2.验收测试

验收测试的目的是判断应用程序是否满足如功能性和易用性等其他需求。在设计/计划阶段,由开发人员和客户来设计验收测试。
验收测试是由客户来执行的。客户对程序是否满足要求进行客观公正的确认。通过使用场景来设计验收测试。使用场景与验收测试通常是一对多。每个使用场景都需要不止一个验收测试。
极限测试中的验收测试可以是自动化或非自动化的。比如,界面颜色是否满足规格说明书,是非自动化。输入公式计算结果的测试是自动化的。
客户使用验收测试来验证程序是否达到预期结果。不一致的话被当做一个缺陷报告开发人员。如果有多个缺陷,则对缺陷的优先级排序。缺陷被修复后客户重新执行验收测试。验收测试也是回归测试的一种形式。
为什么程序可能通过所有的单元测试,却不能通过验收测试。因为单元测试是确认程序单元是否满足规格说明,而非具体的操作性或审美性。理解了规格说明,但不理解其操作性,通常会发生这种情况。

4.3.极限测试的应用

下面通过开发一个小型Java程序,并使用JUnit来描述极限测试的概念。
程序是一个命令行程序,仅判断输入值是否为素数。程序规格说明如下:

开发一个命令行的应用程序,接收一个正整数n(0<=n<=1000),判断n是否为素数。如果n为素数,程序应返回信息,说明其为素数。如果n不是素数,程序也应返回信息,说明其不为素数。如果n不是一个有效的输入,程序应显示一条帮助信息。

首先从设计单元测试开始。对这个程序可以确定出两个具体的任务:确认输入和判断素数。可以分别使用黑盒和白盒测试方法、边界值分析方法和判定覆盖准则。极限测试要求使用一个独立的黑盒测试方法消除所有偏见。

4.3.1.测试用例设计

设计测试用例首先从确认测试方法开始。将使用边界值分析法对输入进行确认,因为程序只能接受固定范围内的正整数,所有其他的输入值,包括字符数据类型和负数都会产生错误,不能被使用。当然,我们可以这样处理本例,将输入确认划归到判定覆盖准则中,因为程序必须判断输入是否有效。在设计测试时必须确定使用某个测试方法

用例编号 输入 预期的输出 注解
1 n=3 确定n为素数 对有效素数的测试;对边界范围内输入的测试
2 n=1000 确定n不为素数 对等于上边界输入的测试;对n不为素数情况的测试
3 n=0 确定n不为素数 对等于下边界输入的测试
4 n=-1 显示帮助信息 对低于下边界输入的测试
5 n=1001 显示帮助信息 对高于上边界输入的测试
6 n=“a” 显示帮助信息 对输入为整数而非字符类型的测试
7 两个或两个以上的输入 显示帮助信息 对输入值的正确个数的测试
8 n为空(空格) 显示帮助信息 对输入值为空时的测试

4.3.2.测试驱动器及其应用

测试驱动器的方法

方法 检查到的测试用例
testCheckPrime_true() 1
testCheckPrime_false() 2,3
testCheck4Prime_checkArgs_char_input() 6
testCheck4Prime_checkArgs_above_upper_bound() 5
testCheck4Prime_checkArgs_neg_input() 4
testCheck4Prime_checkArgs_2_inputs() 7
testCheck4Prime_checkArgs_0_inputs() 8

根据程序规格说明,测试用例,测试配件以及最后的Java程序都由单个check4Prime类组成,其定义如下:

public class check4Prime{
    public static void main(String[] args)
    public void checkArgs(String[] args) throws Exception
    public boolean primeCheck(int num)
}

根据Java程序的定义,main()过程提供了应用程序的入口点。checkArgs()方法判断输入值n是个正数,0<=n<=1000。primeCheck()过程对照一个已计算出的素数列表来检查输入值。采用Eratosthenes筛选法来快速计算素数。由于涉及的素数数量较少,此方法是可以接受的。
代码如下:

package com.huawei;

/*
* 小坑注意:
* 在cmd窗口运行命令行程序时,要回退到com上一级目录执行,例如程序路径为:src\main\java\com\huawei\check4Prime.java
* 要在java目录处执行java com.huawei.check4Prime 5才能正确运行,否则会报"无法加载主类"错误。
* 因为Java会根据打包来查找类
* */
public class check4Prime {
    static final int max = 1000;//Set upper bounds
    static final int min = 0;//Set lower bounds
    static int input = 0;//Initialize input variable

    public static void main(String [] args) {
        //Initial class object to work with
        check4Prime check = new check4Prime();
        try {
            //Check arguments and assign value to input variable
            check.checkArgs(args);
        }
        //Check for Exception and display help
        catch (Exception e) {
            System.out.println("Usage: check4Prime x");
            System.out.println("        -- where 0<=x<=1000");
            System.exit(1);
        }
        //Check if input is a prime number
        if (check.primeCheck(input)) {
            System.out.println("Yippeee... " + input + " is a prime number!");
        } else {
            System.out.println("Bummer... " + input + " is NOT a prime number!");
        }
    }//End main

    //Calculates prime numbers and compares it to the input
    public boolean primeCheck(int num) {
        double sqroot = Math.sqrt(max);//Find square root of n
        //Initialize array to hold prime numbers
        boolean primeBucket[] = new boolean[max + 1];
        //Initialize all elements to true, then set non-primes to false
        for (int i = 2; i <= max; i++) {
            primeBucket[i] = true;
        }
        //Do all multiples of 2 first
        int j = 2;
        for (int i = j + j; i <= max; i = i + j) { //start with 2j as 2 is prime
            primeBucket[i] = false; //set all multiples to false
        }
        for (j = 3; j <= sqroot; j = j + 2) { //do up to sqrt of n
            if (primeBucket[j] == true) { //only do if j is a prime
                for (int i = j + j; i <= max; i = i + j) { //start with 2j as j is prime
                    primeBucket[i] = false; //set all multiples to false
                }
            }
        }
        //Check input against prime array
        if (primeBucket[num] == true) {
            return true;
        } else {
            return false;
        }
    }//End primeCheck()

    //Method to validate input
    public void checkArgs(String [] args) throws Exception{
        //Check arguments for correct number of parameters
        if (args.length != 1) {
            throw new Exception();
        } else {
            //Get integer from character
            Integer num = Integer.valueOf(args[0]);
            input=num.intValue();
            //If less than zero
            if (input < min) { //If less than lower bounds
                throw new Exception();
            } else if (input > max) { //If greater than upper bounds
                throw new Exception();
            }
        }
    }
}//End check4Prime

输出结果:

java com.huawei.check4Prime 5
Yippeee... 5 is a prime number!

java com.huawei.check4Prime 10
Bummer... 10 is NOT a prime number!

java com.huawei.check4Prime A
Usage: check4Prime x
        -- where 0<=x<=1000
package com.huawei;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;

@TestMethodOrder(OrderAnnotation.class)
class check4PrimeTest {
    //Initialize a class to work with.
    private check4Prime check4Prime = new check4Prime();


    @Test
    @Order(1)
    //Test case 1
    public void testCheckPrime_true() {
        assertTrue(check4Prime.primeCheck(3));
    }

    @Test
    @Order(2)
    //Test case 2,3
    public void testCheckPrime_false() {
        assertFalse(check4Prime.primeCheck(0));
        assertFalse(check4Prime.primeCheck(1000));
    }

    @Test
    @Order(4)
    //Test case 4
    public void testCheck4Prime_checkArgs_neg_input() {
        try {
            String[] args = new String[1];
            args[0] = "-1";
            check4Prime.checkArgs(args);
            fail("Should raise an Exception.");
        } catch (Exception success) {
            //successful test
        }
    }

    @Test
    @Order(5)
    //Test case 5
    public void testCheck4Prime_checkArgs_above_upper_bound() {
        try {
            String[] args = new String[1];
            args[0] = "1001";
            check4Prime.checkArgs(args);
            fail("Should raise an Exception");
        } catch (Exception success) {
            //successful test
        }
    }

    @Test
    @Order(6)
    //Test case 6
    public void testCheck4Prime_checkArgs_char_input() {
        try {
            String[] args = new String[1];
            args[0] = "r";
            check4Prime.checkArgs(args);
            fail("Should raise an Exception.");
        } catch (Exception success) {
            //successful test
        }
    }

    @Test
    @Order(7)
    //Test case 7
    public void testCheck4Prime_checkArgs_2_inputs() {
        try {
            String[] args = new String[2];
            args[0] = "5";
            args[1] = "99";
            check4Prime.checkArgs(args);
            fail("Should raise an Exception.");
        } catch (Exception success) {
            //successful test
        }
    }

    @Test
    @Order(8)
    //Test case 8
    public void testCheck4Prime_checkArgs_0_inputs() {
        try {
            String[] args = new String[0];
            check4Prime.checkArgs(args);
            fail("Should raise an Exception.");
        } catch (Exception success) {
            //successful test
        }
    }
}

输出结果:

5.总结

严格遵循敏捷开发过程,为我们提供了一条更快速、更高品质的软件开发道路,比传统软件开发方法更有效率。
极限编程模型是主流敏捷开发方法之一,这种轻量级的开发过程主要把目光集中于沟通、计划及测试。极限编程中的测试称为"极限测试"。极限测试的重点在于单元测试和验收测试。一旦代码库发生变化,就需要进行单元测试。在重要的发布节点,由客户执行验收测试。
极限测试还要求程序员在开始编码之前,要根据程序的规格说明设计测试用例。在这种方式中,开发的程序要通过单元测试,从而提高该程序满足其规格说明的概率。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。