BUAA-OO-Unit3

冉啟何-22373028 学生 2024-05-16 14:43:43

BUAA-OO-Unit3

一、本单元测试过程

(一)黑箱测试与白箱测试

黑箱测试,通过测试来检测每个功能是否能正常使用。在不考虑程序内部结构和内部特性的情况下,检查程序功能是否按规定正常使用,检查程序是否能适当地接受输入数据而产生正确的输出信息。

简单而言,就是通过一系列的输入跟输出,判断程序是否正常工作。在本单元而言,就是从给定输入,判断给定输出是否跟期望一致。

白箱测试,是在全面了解程序内部结构逻辑的情况下,对所有逻辑路径进行测试。测试者从检查程序的逻辑入手,清楚程序内部运行逻辑,而对功能要求不做检查。

简单而言,就是“读代码”,从代码逻辑出发,分析逻辑是否出错。

(二)单元测试、功能测试、集成测试、压力测试、回归测试

1、单元测试

单元测试,是指对程序中最小可测试单元进行检查与验证。

单元可以是函数、方法、类、模块、子系统。它是一种静态的白箱测试,因为两者都是对代码进行测试,都是了解相关代码内部的逻辑节后,对其中各个逻辑路径进行测试。

2、功能测试

功能测试,是根据产品特性、操作描述和用户方案,测试一个程序的功能或者某个过程的功能是否能按照期望的方式正确运行。

功能测试就是黑箱测试,只用测试各个功能是否正确运行,而不考虑内部结构以及代码。

3、集成测试

集成测试,也叫组装测试或者联合测试,是在单元测试的基础上,将所有模块按照设计要求组装成为子系统或系统,进行集成测试。

集成测试建立在单元测试的基础之上,有时4各个模块或许能单独地进行正常地工作,但连接起来也不一定能正常地工作。单元测试反映局部问题,集成测试反映全局问题。

4、压力测试

压力测试,也称强度测试、负载测试。模拟实际应用地软硬件环境以及用户使用过程中地系统负荷,长时间或大负荷地测试程序,来测试被测系统地性能、可靠性、稳定性。

压力测试是保证程序正确运行地情况下,对程序地运行效率与可靠性地检测。

5、回归测试

回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。

当原有程序发生改变时,程序就有可能发生错误。因此,每当有新代码加入软件时,就需要重新测试现有地功能,检查修改是否达到预期,以及修改是否损害了原有地功能。

(三)数据构造

数据构造会对特定的功能构造特定的数据。

对于仅仅对图相关算法的计算,就是创建一个随机的图,输入后,判断查询图的属性的功能是否正确运行。对这种测试进行多次循环。

    private HashSet<Vec> getVecs(int num) {
        Random random = new Random();
        HashSet<Vec> vecs = new HashSet<>();

        while (vecs.size() < num) {
            int x = random.nextInt(1000) + 1;
            int y = random.nextInt(1000) + 1;
            if (x == y) {
                continue;
            }
            Vec v = new Vec(x, y);
            boolean have = false;
            for (Vec v1 : vecs) {
                if (v1.equals(v)) {
                    have = true;
                    break;
                }
            }
            if (have) {
                continue;
            }
            vecs.add(v);
        }
        return vecs;
    }
    public class Vec {
        private int x;
        private int y;
    }

对于发送信息的功能,则减少 person 数量,增加发送信息的次数,着重查询信息发送前后,各个 person 状态是否符合期望。

二、图模型构建与维护策略

(一)图模型构建

对于课程组已经提供的类或接口,图结构由 MyPersonMyNetwork建构,每个 personacquaintance 为与其相连的点,对应的value为相应的权重。

Graph 存储了一个邻接矩阵,主要作用是查询最大连通子图。

public class Graph {
    private HashMap<Integer, HashSet<Integer>> group = new HashMap<>();
}

Edges存储了边的信息,来方便对边进行遍历操作。

public class Edge {
    private int nodeMin;
    private int nodeMax;
    private int value;
}

/* MyNetwork.java */
private HashSet<Edge> edges = new HashSet<>();

(二)维护策略

在这单元作业中,存在大量的对图数据进行的修改,以及查询,此时对一些查询变量进行动态维护就十分重要。

例如 tripleSum ,若查询时重新计算,则一次查询是 O(n^3) 的时间复杂度,而若进行动态维护,在加边与删边操作时修改数据,则修改时为 O(n) 的复杂度,查询时仅仅为 O(1) 的复杂度。

    private void flushTripleSum(int id1, int id2, boolean add) {
        int idMin = graph.getMinSetId(id1, id2);
        int idMax = idMin == id1 ? id2 : id1;
        HashSet<Integer> minSet = graph.getSet(idMin);
        int num = 0;
        Person person = getPerson(idMax);
        for (Integer p : minSet) {
            if (person.isLinked(getPerson(p))) {
                num++;
            }
        }
        if (add) {
            tripleSum += num;
        } else {
            tripleSum -= num;
        }
    }

同样的,还有计算 ageVarageMean 时,可以动态维护,在这些值没有变化时,设置脏位为0,变化时脏位置1,查询时,若脏位为1,就重新计算。

三、性能问题及修复情况

(一)性能问题

这次的性能问题主要集中在图有关的算法之中。比如计算最大连通子图的数目与计算是否存在最短路径时使用并查集,计算最短路径的长度时使用 dijkstra 算法。

另外就是动态维护做的不够好,导致第一和第二次作业均有 ctle 的问题。比如反复使用 qtvs 若没有做好维护,就会出现每次查询时遍历一遍。

(最臭的一集)

......
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
qtvs 114514 1919810
......

(二)修复情况

这些性能问题在bug修复阶段均得到了修复。

四、规格与分离的理解

规格是程序开发者对程序功能的约束,对输入、输出、副作用进行了规定。但实现肯定不能照着做,比如计算三元环的数目时,规格实现的是三重循环,时间复杂度较高,但为了减少时间开销,我们选择的是动态维护。实现要跟规格分离,但只要输入、输出、副作用满足规格限制,实现就是没有问题的。

计算三元环的规格

    /*@ ensures \result ==
      @         (\sum int i; 0 <= i && i < persons.length;
      @             (\sum int j; i < j && j < persons.length;
      @                 (\sum int k; j < k && k < persons.length
      @                     && getPerson(persons[i].getId()).isLinked(getPerson(persons[j].getId()))
      @                     && getPerson(persons[j].getId()).isLinked(getPerson(persons[k].getId()))
      @                     && getPerson(persons[k].getId()).isLinked(getPerson(persons[i].getId()));
      @                     1)));
      @*/
    public /*@ pure @*/ int queryTripleSum();
    public int queryTripleSum() {
        return tripleSum;
    }

五、Junit测试

这三次作业均要求使用Junit对一个方法进行测试。这是一种白盒测试,也是单元测试。

测试中主要分为两个部分。一部分是数据生成,另一方面是结果对比。

数据生成主要是随机生成的,并多次测试,力求覆盖情况。

结果对比就是对规格中的各个 ensure 句的情况进行判断,以及对副作用,如 pure 条件的判断。

六、学习体会

本单元学习的重点内容是 JML 语言。其次实现就是图算法相关的实现。

但我这单元做的并不好,主要是时间复杂度上没有把握好。导致第一次作业中多个点ctle,其次就是细节上实现的不够好,导致代码有逻辑错误。

这个单元最大的感受就是在做一个巨大的代码补全,跟着 JML ,实现一个个方法,在此过程中 JML 做到的作用便是精确地描述方法的功能与副作用

JML 最大的方便就是在编写 Junit 测试时,能明确如何判断一个方法是否符合预期。

...全文
45 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧