软件工程第三次作业

1.题目的选择

这次的作业有两个题目可以选择,我选择了第一题:

问题: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n
例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。

2.程序代码

我用了c语言编译程序,思路是将每种结果都计算出来进行比较,选择其中最大的,这种计算方法时间复杂度比较高,且浪费空间,但是算法思想比较简单,便于接受,代码如下:
image


3.覆盖标准与测试用例

以下是各种覆盖标准的优缺点:

(1)语句覆盖:

设计若干测试用例,运行被测程序,使程序中每个可执行语句至少执行一次。只需设计一个测试用例:a=2,b=1,c=6;即达到了语句覆盖。

【优点】 :可以很直观地从源代码得到测试用例,无须细分每条判定表达式。

【缺点】 :由于这种测试方法仅仅针对程序逻辑中显式存在的语句,但对于隐藏的条件是无法测试的。如在多分支的逻辑运算中无法全面的考虑。语句覆盖是最弱的逻辑覆盖。

(2)判定覆盖:

设计若干测试用例,运行被测程序,使得程序中每个分支的取真值和取假值至少一次,即判断真假值均曾被满足。a=2,b=1 ,c=6(M,Q分支全为真)和a=-2,b=-1 ,c=-3(M,Q分支全为假)这两组测试用例可覆盖所有判定的真假分支。

【优点】:判定覆盖具有比语句覆盖更强的测试能力。同样判定覆盖也具有和语句覆盖一样的简单性,无须细分每个判定就可以得到测试用例。

【缺点】:往往大部分的判定语句是由多个逻辑条件组合而成,若仅仅判断其整个最终结果,而忽略每个条件的取值情况,必然会遗漏部分测试路径。判定覆盖仍是弱的逻辑覆盖。

(3)条件覆盖:

设计若干测试用例,执行被测程序以后要使每个判断中每个条件的可能取值至少满足一次。
判断M表达式:设条件 a>0 取真 记为 T1 ;假F1
条件 b>0 取真 记为 T2 ;假F2
判断Q表达式:设条件 a>1 取真 记为 T3 ;假F3
条件 c>1 取真 记为 T4 ;假F4
我们用条件覆盖设计的思想就是让测试用例能覆盖T1、T2、T3、T4、F1、F2、F3、F4

【优点】:增加了对条件判定情况的测试,增加了测试路径。

【缺点】:条件覆盖不一定包含判定覆盖。例如,我们刚才设计的用例就没有覆盖判断M的Y分支和判断Q的N分支。条件覆盖只能保证每个条件至少有一次为真,而不考虑所有的判定结果。

(4)判定-条件覆盖:

设计足够的测试用例,使得判断条件中的所有条件可能至少执行一次取值,同时,所有判断的可能结果至少执行一次。
测试用例要满足如下条件:1.所有条件可能至少执行一次取值;2.所有判断的可能结果至少执行一次。

【优点】 :能同时满足判定、条件两种覆盖标准。

【缺点】 :判定/条件覆盖准则的缺点是未考虑条件的组合情况。

(5)条件组合覆盖:

设计足够的测试用例,使得所有可能的条件取值组合至少执行一次。

【优点】 :条件组合覆盖准则满足判定覆盖、条件覆盖和判定/条件覆盖准则。

【缺点】 :线性地增加了测试用例的数量。

(6)路径覆盖:

设计所有的测试用例,来覆盖程序中的所有可能的执行路径 。

【优点】 :这种测试方法可以对程序进行彻底的测试,比前面五种的覆盖面都广。

【缺点】 :需要设计大量、复杂的测试用例,使得工作量呈指数级增长,不见得把所有的条件组合都覆盖。

从前面的例子我们可以看到,采用任何一种覆盖方法都不能满足我们的要求,所以,在实际的测试用例设计过程中,可以根据需要将不同的覆盖方法组合起来使用,以实现最佳的测试用例设计 。

综上所述,我选择了条件组合覆盖:
下面是流程图:

image

下面是测试代码:

#include <stdio.h>
#include <iostream>
#include<cstdio>
#include<gtest/gtest.h>


using namespace std;
int o[200]={0,2,5,8,-9,6},i[200]={0,-8,-9,7,10,-9},u[200]={0,20,-9,-6,15,8};
//N是数组长度,num是待计算的数组,放在全局区是因为可以开很大的数组
int N,num[1024];

int tested(int N,int num[200])
{
    int i,j,s,k,ans;
    //输入数据
     ans = num[1]; //ans保存最大子序列和,初始化为num[1]能保证最终结果正确
    //i和j分别是枚举的子序列的起点和终点,k所在循环计算每个子序列的和
    for( i = 1; i <= N; i++) {
        for( j = i; j <= N; j++) {
             s = 0;
            for( k = i; k <= j; k++) {
                s += num[k];
            }
            if(s > ans) ans = s;
        }
    }
     return ans;
}
TEST (tested,HandleNoneZeroInput)
{

    EXPECT_EQ(15,tested(5,o));
    EXPECT_EQ(17,tested(5,i));
    EXPECT_EQ(28,tested(5,u));

}
int main(int argc, char *argv[])
{

   testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
    cout<<tested(5,o);
    return 0;
}

从代码中可以看出我测试了三个用例:
image

以下是测试结果:
image
三个测试用例都完全正确


4.小结

这次的编程题涉及到一些算法,由于我本身对java的不熟悉,所以用了codeblocks编译器来编写c程序。同时又重新学了一遍codeclocks用Googletest做单元测试,主要还是借鉴了其他同学的博客。做完这次作业之后,我深深的感觉到多学一门编程语言的重要性,只会用c编程实在是太受限制了。不说了,去学java了......


5.附录

coding地址:
https://coding.net/u/houzhuzhu/p/homework333/git/blob/master/q23.cpp?public=true

原文地址:https://www.cnblogs.com/jing-hao/p/8664871.html