第三单元博客总结

第三单元博客总结

单元测试的过程

  • 黑箱测试:黑箱测试是一种测试方法,它不考虑内部的代码或设计,而是将软件系统视为一个黑箱,只测试其输入和输出的功能和性能。黑箱测试是一种功能测试方法,它通过输入一组测试数据并检查输出结果来验证软件系统是否符合预期的功能和性能要求。黑箱测试可以帮助发现软件系统中的缺陷,提高软件质量。在中测与弱侧中,我们只是将代码丢进去让评测机测评并反馈结果,不需要关心代码的内部结构,只需通过输入和输出判断代码是否正确。使用评测机生成随机数据测评也属于黑箱测试,在课下的测试中,我认为大多数都是黑箱测试,黑箱测试能很快的反应出代码的错误,提高效率。

  • 白箱测试是一种测试方法,它基于对软件系统内部的代码、结构和设计的了解,通过检查代码和程序逻辑来测试软件系统的功能和性能。白箱测试通常由开发人员或专业测试人员执行,旨在发现代码中的缺陷和错误。白箱测试可以帮助开发人员识别和解决代码中的问题,提高软件的质量和可靠性。白箱测试通常包括单元测试、集成测试和系统测试等不同层次的测试。白箱测试相当于作业中的互测中查看别人的代码然后通过造出相应的数据使得代码错误能够显现出来。查看别人代码过程中需要逐步分析别人代码的结构、逻辑和算法,这是一个耗时的测试过程,但往往也是找到bug的最佳测试方法,在课下测试中,我们使用黑箱测试判断代码是否有问题,然后使用白箱测试找到代码的问题所在,两种方法结合在一起才能完成真正的测试。

  • 单元测试:单元测试是指对软件系统中最小的可测试单元进行测试,通常是对一个函数、方法或模块进行测试。单元测试主要用于验证代码的正确性和可靠性,以及检查代码是否符合预期的行为和功能。很遗憾在本单元作业中我并没有完全进行单元测试,主要原因是本单元每次作业都是实现很多个函数,如果要进行单元测试,得编写过多的测试程序来进行测试,虽然课程组提供了Junit来帮助,但是该测试方法是基于我们对jml理解完全无误的情况下才能完成测试,如果理解错误那么Junit必将也编写错误,那么也就是是说我们用我们理解的错误测试方法,去测试错误的代码,也即是自己既当运动员也当裁判,那多半是检测不出来什么问题。我觉得最好的方法就是把裁判和运动员分开,也就是常说的对拍,两个人通过黑箱测试来进行判断输出是否相同,虽然这样也不能找出双方共同的bug,但是如果有输出不同,那必定有一方代码有bug。

  • 功能测试:功能测试是指对软件系统的功能进行测试,以验证软件是否符合用户需求和规格说明书中的要求。测试过程中需要模拟各种情况和输入,以确保软件系统能够正确地处理各种情况和输入。在本单元作业中,是否满足功能测试也就是输入指令之后,输出是否满足预期以及维护的内存数据是否正确。

  • 集成测试:集成测试是指将多个单元或模块组合成一个整体进行测试,以验证它们之间的接口和交互是否正确。测试过程中需要模拟各种情况和输入,以确保整个系统能够正确地处理各种情况和输入。本单元中,许多指令会相会影响,所有进行集成测试是十分必要的,最简单的方法就是排列组合,让指令之间以各种组合进行测试,当然这个最重要的还是编写一个强大的数据生成器,能尽可能满足所有指令之间的组合。

  • 压力测试:压力测试是指对软件系统进行负载测试,以验证软件系统在高负载情况下的性能和稳定性。当然,强测,妥妥的压力测试。当然,其实还可以更强,必须现实中的网络关系可能远远不止10000条指令,可能是几亿条。

  • 回归测试:回归测试是指在软件系统发生变化或升级后,对已经测试过的功能进行再次测试,以确保变化或升级不会影响已经测试过的功能和性能。基本上就是找bug,找到之后修改代码,再进行测试。

    了解这些测试之后,再结合我们单元作业,发现作业课下和课上的测试基本上包含了所有的这些测试,可以感受到课程组的用心。

    是否使用了测试工具

    本单元中主要还是使用对拍器来测试自己和别人代码是否有问题,在本单元开始之前还想搭建一个测评机,但是发现本单元的测试很难通过侧面来反应出自己代码的正确性,如果要测试,估计也就是自己用其他的语言在写一份代码,但逻辑也都是一样的,也就是运动员和裁判员是同一个人,况且本单元主要考察就是对jml语言的理解,所以无法保证自己理解的jml语言就是正确的,最好的方法就是参考别人对jml语言的理解对照自己的理解,所有在本单元中我大多使用对拍来反应双方对jml语言的理解是否一致,从而找出代码中的bug。

    数据构造有何策略

    本次数据构造基本上很暴力:随机数据生成。虽然这样生成的数据会导致很多无效的数据,但是只要数据开的够大(十万条数据),还是能找到特定组合的问题,就是定位bug的时候确实不好找,毕竟测试数据实在太大,但是可以找到是哪一条指令出了问题,然后在定位到解析该指令的代码处和jml对比进行代码走查,一般进行代码走查可以找到该条指令的问题,但如果该指令的错误是由于其他指令导致的,那就。。。。很麻烦了,需要再构造大量该指令和其他指令的组合测试数据来进行测试,当然可以只与会影响该指令的指令进行组合,这样就缩减了组合的复杂度,快速找出代码存在的问题。

    对于一些特定的指令,如第11次作业中的qlm指令,这条指令如果图太稀疏的话确实很难找到该条指令的bug,所以得想方设法的使得图足够稠密,比如在最开始我们先加一大堆的人,把这些人的id加入到一个容器当中,在加关系的时候我们只从这个容器中选人来加关系,这样就会使得构建的图会十分稠密了。

    梳理本单元的架构设计,分析自己的图模型构建和维护策略

在这里插入图片描述

本单元中主要还是维护一个NetWork实例,Network中包含Message、Group和Person的单个容器,这三个容器在Network中构建成一个庞大的关系网。其实在本单元中,Message、Group和Person我全部使用HashMap数据结构存放,这样的好处就是使得查找十分迅速,复杂度为O(1),添加时也很方便。对于维护策略,说实话,我一直严格按照jml编写的规格进行书写代码,我认为按照jml进行写代码应该也算是维护了这些容器。

在第九次作业中,我们很多修改关系的指令都是添加操作很少有删除操作,所以在第9次作业中,使用并查集可以很好的解决isCycle指令函数的问题,但是在第十次作业中,添加了修改关系的指令,导致很有可能会删除掉两个人之间的关系,这导致并查集在第十次作业中并不是那么好用,进而导致第九次作业到第十次作业的迭代过程中有较大的修改,我甚至是重构了一份,这让我深刻体会到一定要挖掘潜在需求的重要性,使得自己的代码可迭代行强。

分析作业中出现的性能问题及其修复情况,谈谈自己对规格与实现分离的理解

在第九次作业中,我对jml语言理解还不是很到位,没有理解规格与实现分离的概念,我认为实现一定要严格按照jml所描绘的方法来实现(其实也是把jml语言当成了实现的方法),没有对实现的方法进行优化,所以导致了第九次作业跑的时间很长。然后慢慢才明白规格与实现相分离的含义,规格其实只是为了保证该函数输入输出正确,如果按我的理解是,jml语言就告诉你,输入是什么样、输出该是什么样,并且实现方法的时候什么可以改、什么不可以改变,也就是告诉我们接口,让我们实现内部的代码,至于使用什么方法、算法和数据结构,这无所谓,只要满足它的接口即可。所以在实现中数据结构、算法这些都是在满足规格的要求下怎么方便怎么来。

数据结构方面,使用大量的HashMap结构来减少时间复杂度。

 private HashMap<Integer, Person> people;
 private HashMap<Integer, Group> groups;
 private HashMap<Integer, Message> messages;

第九次作业中使用并查集和动态优化提高isCycle和qbs的效率。

	 public int find(int id) {
        int ancestor = id;
        while (ancestor != parent.get(ancestor)) {
            ancestor = parent.get(ancestor);
        }
        int now = id;
        while (ancestor != now) {
            int father = parent.get(now);
            parent.put(now, ancestor);
            now = father;
        }
        return ancestor;
    }

    public void addNode(int id) {
        if (!parent.containsKey(id)) {
            blockNum++;  //动态优化
            parent.put(id, id);
            distance.put(id, 1);
        }
    }

    public void union(int id1, int id2) {
        int rootId1 = find(id1);
        int rootId2 = find(id2);
        if (rootId2 != rootId1) {
            blockNum--;  //动态优化
            if (distance.get(rootId1) < distance.get(rootId2)) {
                parent.put(rootId1, rootId2);
            } else if (distance.get(rootId1) > distance.get(rootId2)) {
                parent.put(rootId2, rootId1);
            } else {
                parent.put(rootId2, rootId1);
                distance.put(rootId1, (distance.get(rootId1) + 1));
            }
        }
    }

本次性能出问题主要是在第十一次作业中的qlm指令上,我最开始的思路比较暴力,我采用得是将出发点与自己的亲戚删除,然后通过找出发点到亲戚的最短路线,这种需要对出发点的每一个亲戚都做一次dijkstra,导致时间复杂度过高,所以tle。该方法的时间复杂度应该为出发点的亲戚数m乘以一次跑dijkstra的复杂度(n + m)log(n)。在修复中我参考了讨论区中的改良版的dijkstra算法,该算法就是找到一个最短路劲和次短路径,这样跑一次dijkstra即可,大大优化了查询复杂度。

 public Path(int distance1, int distance2, int origin1, int origin2, int id) {
        this.distance1 = distance1;   //最短路径
        this.distance2 = distance2;   //次短路径
        this.origin1 = origin1;       //最短路径出发点
        this.origin2 = origin2;       //次短路径出发点
        this.id = id;
    }
    
public int updata(int distance, int origin, int value) {
        if (distance == Integer.MAX_VALUE) {
            return 0;
        }
        if (distance + value < this.distance1) {
            if (origin != this.origin1) {
                this.origin2 = this.origin1;
                this.distance2 = this.distance1;
            }
            this.origin1 = origin;
            this.distance1 = distance + value;
            return 1;
        } else if (distance + value < this.distance2) {
            if (origin != this.origin1) {
                this.origin2 = origin;
                this.distance2 = distance + value;
            }
            return 0;
        }

        return 0;
    }

本单元中同学们实现了OK测试方法,请同学们思考OK测试对于检验代码实现与规格的一致性的作用,有何改进何建议

OK测试是一种基于形式化规格的测试方法,它主要用于检验代码实现与规格的一致性。在OK测试中,首先通过形式化规格描述软件系统的行为和功能,然后通过自动生成测试用例来检验代码实现是否符合规格要求。其实在我看来,OK测试最大的好处是告诉我,我编写的代码是不符合哪一条ensure,这可以让我明白我对jml规格的哪一个部分不理解,进而定位到bug出现的地方。

本单元学习体会

  • 本单元中学习到了形式化语言jml:jml是一种用于Java程序中的形式化规范语言,它提供了一种标准的方式来描述Java程序的行为和功能。JML语言可以用于描述Java程序的前置条件、后置条件、不变式等方面的行为和功能,从而帮助开发人员和测试人员更好地理解和验证Java程序的正确性和可靠性。jml最大的好处就是使得要求没有二义性,而且还可以保证代码的正确性。
  • 加深了自己图论的知识:在本单元中,学习到了并查集、连通图。更加深了自己对dijkstra的理解。
    变式等方面的行为和功能,从而帮助开发人员和测试人员更好地理解和验证Java程序的正确性和可靠性。jml最大的好处就是使得要求没有二义性,而且还可以保证代码的正确性。
  • 加深了自己图论的知识:在本单元中,学习到了并查集、连通图。更加深了自己对dijkstra的理解。
  • 针对需求选择最佳的算法。本单元中并没有给出我们明确的指示需要使用什么样的算法,有的只是告诉我们需要满足什么样的输入输出接口,这就让我们开发人员自己去理解用户的需求选择合适的算法实现需求。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值