OO第三单元博客

OO第三单元博客

测试

黑盒测试与白盒测试

  • 黑盒测试:粗略地说,就是当测试人员在不知道系统的内部工作情况下进行测试工作,这种方法则称为黑盒测试。在这种情况下,被测系统被视为“黑匣子”。需求文档或功能规范文档构成了此测试的基础,这要求测试者了解软件的流程。

  • 白盒测试:在白盒测试方法中,测试人员已经了解了系统内部的构造,并清晰地知道系统是如何实现的。 测试人员利用这些知识去开发测试用例,以用来检查控制流,信息流,数据流,异常和错误处理和系统的编码实践等

各类其他测试

  • 单元测试:对软件中的最小可测试单元进行检查和验证,是测试的最基本形式,确保每个单元都能正确完成功能。

  • 功能测试:测试一个软件的功能是否符合规格要求,是否符合完成了应该的功能,是否修改了不该修改的内容

  • 集成测试:又称组装测试组件间的接口与交互测试。是在单元测试的基础上,按照设计要求,把单元测试通过的单元组成系统或子系统而进行的有序的测试

  • 压力测试:主要测试被测系统持续的在一定的压力下,例如CPU或内存在饱和使用的情况下,系统能够处理问题的能力。

  • 回归测试:回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。自动回归测试将大幅降低系统测试、维护升级等阶段的成本。

构造数据

  • 高覆盖率:测试每个方法的各个分支,正常异常情况,看看方法是否正确实现

  • 边界数据:覆盖数据范围的极端,如整数的边界,tag人数的上限。

  • 压力测试:针对于某一功能的大量测试,测试其功能是否正确,测试时间是否符合要求。

  • 随机策略:测试多个功能之间是否存在问题,因此数据需要广泛覆盖,并且多轮测试,一般采用随机生成。

架构设计

本次的三次作业,是根据课程组给出的JML规格,实现自己的方法。所以具体的迭代逻辑由助教来实现,所以减少了很多删改的过程,第一次完全不用重构,第一次体验到好的迭代写起来有多么的舒适。

总体架构

三次作业的大体架构没有什么大的变化,只是第二次增加了Tag,第三次增加了Message,以及对应的一些Exception。

整体的业务逻辑全部在network中,配合Tag,Message,Person三个类以及Exception异常来实现所有功能。第一次作业为了更好的满足规格要求,新建立了UnionFind和Link类。为了更好的实现异常类的计数,新建了一个ExceptionCount类,来保存id与对应出现的次数。

作业难点

第一次作业

在第一次的作业中,较为复杂的就是测试两点是否连通,以及有多少个三角形。

连通

判断图的连通,使用并查集是最快的,查询增边的复杂度都不会超过O(n)。并查集中有两种优化方式,一种是按秩合并,一种是路径压缩。路径压缩后查询是O(1)复杂度,而按秩合并增边的复杂度是O(1)。这里附上并查集的教程

但是并查集对于删边这种情况不能处理,因为它是图的一种不完全映射,只保留了部分的图的条件。因此删边的时候,需要额外的条件来介入。

  • DFS

    • 删边时在图中遍历,看是否有另一条路径可达,可达则在并查集中不删,但是这种只能用最基本的并查集,使得删去后可以得到两个正确的非连通图。

    • 注意不要使用递归的DFS,要用堆栈的方式,递归有可能爆栈

  • 脏位与重建

    • 由于删边会使之前的失效,那就重建一个新的并查集!前一个DFS的时间复杂度是O(n+m),而重建一个并查集的复杂度也是O(n+m),还可以使用优化后的并查集,更有优势。

    • 在重建时,需要使用到之前的所有边。我边的容器集合使用了Hashset<Link> linksLink是自己实现的类,保存边的两个点的id,为了方便从Hashset中删边,在Link中重写了hashcode以及equals。遍历personslinks就能重新构建。

    • 为了对于重建这一动作,其实并不是每次删边时都要去进行的,只需要在查询前重建即可。因此设置一个脏位dirty,删边后设true,此时,不再维护并查集,只维护personslinks,在查询时先重建再将dirty重新置为false

TripleSum

原来的TripleSum的计算方式是,三重循环遍历所有的点,查询是否能构成三角形。O(n^3)的复杂度显然不可取,为了降低复杂度,使用TriNum属性来保存Network中的三角形个数,每次修改边时,只需要遍历一个顶点的所有acquaintance,计算与另一个顶点直接相连的个数,增减这一数值即可。

//For instance in addRelation
public void addTri(int id1, int id2) {
    HashMap<Integer, Person> acqs = ((MyPerson) getPerson(id1)).getAcquaintance();
    for (Integer i : acqs.keySet()) {
        if (acqs.get(i).isLinked(getPerson(id2)) && acqs.get(i).getId() != id2) { 
            triNum++;
        }
    }
}

第二次作业

在第二次作业中,增加了Tag,较为复杂的是TagValueSum以及BestAcquaintance

TagValueSum

TagValueSum计算了Tag内的所有熟人的value之和,因此,其修改有三部分,向tag中加人减人,修改tag中某两个人的关系的value值,前两者实现较为简单,在修改人时遍历剩余的所有人即可。而后一部分,为了简单,在NetWork中增加Hashset<tag> tags属性,修改时遍历所有tag,调整其valueSum

BestAcquaintance

BestAcquaintance记录了其的熟人中value最高的最小id的那个人。我是设置两个多余的属性bestId,bestValue来保存这一值,在修改时进行维护。而在讨论后,得知还有一种优先队列的数据结构,可以通过java内置的内容直接维护。

由于CoupleSum的查询时会使用每个人的BestAcquaintance,所以将其在CoupleSum查询的内部,用一个数组保存起来,避免重复调用函数。

第三次作业

第三次作业增加了Message类,实现了单发与群发消息,没有算法上的难度,只需要将JML正常翻译就能通过。

唯一需要注意的就是第三次作业中的JML太长,需要认真的阅读转化,稍有不慎,就可能产生问题。

规格与实现分离

规格的要求,是一种契约化编程,它要求了我们对于某个东西的实现的抽象的要求,避免了自然语言的二义性,能够让我们更好的理解实现的逻辑规范。

而实现则是更为具体,是我们将抽象的规格要求,使用具体的java语言来满足对应要求的过程。

两者的分离,使得要求与具体实现分离,使得某个功能的实现分为了两个迭代步,先设计好此功能要满足那些要求,先画好一个靶,第二步再在已有的规格下,选取合适的方式进行实现。这样的分离,降低了思维的难度,避免一边想具体实现,一边去想要求如何满足,减少忽略一些条件的可能,减少一些功能不完善的可能。

Juint测试

juint其实就是一个对拍工具,需要自己捏数据,自己根据数据先得到一个答案,再逐一对比结果。主要测试内容为:每条ensures,assignable之外的值是否被修改,以及返回值。

  • 数据构造

    • 对于测试,首先需要自己来捏造数据,捏造的数据要能覆盖到程序中的问题,构造精简而富有针对性的数据有点过于困难,因此选取大力出奇迹,使用大量的随机数据。要根据测试方法进行选择构造的数据大小,类型。

    • 例如对deleteColdEmoji方法,主要在于测试发送消息,删消息。因此图可以开的较小,产生较多的数据,再随机产生大量的多类型的消息,选取一部分进行发送,最后生成一个limit。

  • 自行实现JML要求

    • 这一步,需要将jml里面的规格自己进行实现,以得到规定的正确答案。

    • 为了避免两次计算(自己的以及要测试的)带来的相互影响,使用两个深拷贝的数据,分别进行两个不同的计算,得到结果。

  • 断言

    • 先对于每个能修改的内容,修改是否符合规范

    • 对于不可修改的内容,检查前后是否一致

    • 对于计算结果或返回值,检查是否相同

学习体会

本单元的实现,使我第一次的体会到了真正的增量化开发是什么样的,原来可以这样的舒适,对于原来的部分几乎不需要重构。同时,也感受到了,规格和实现分离的力量,正如前文所说,规格与实现的分离,使得问题的思考可以更加全面,对于边界情况的定义区分更加的直白了当。

关于测试的Juint部分,虽然测试起来较为麻烦,但是通过几次的尝试,亲身体会到了白盒测试的操作过程,了解其过程需要注意的一些细节,更是在实现中逐渐了解到什么样的数据才能更好的测试出程序的bug。

有关JML的部分,老师说这种规格确实写起来复杂,但是这是要用在刀刃上的好钢,是不可替代性的体现,希望自己记住这一单元jml的学习经验,并将其用在日后的学习工作之中。

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值