BUAA_OO_第三单元总结

顾檬-19373765 2024-05-19 12:34:29

 

黑箱测试:评测机反馈是一种黑盒测试。

白箱测试:Junit测试即是一种白盒测试。


单元测试:单元测试是最基础的测试方法,针对每个类和方法进行了独立的验证。通过JUnit框架,课程组三次作业中要求我们对三次作业中的三个函数进行单元测试


功能测试:功能测试注重整个系统的功能性,通过模拟实际使用场景来验证系统的完整性。例如,第二次迭代中Tag部分的功能和BestAcquaintance部分的功能无关,可以选择只针对其中一方面做高强度的测试。


集成测试:集成测试旨在验证各模块之间的协作性。在本单元中,特别是社交网络和用户类之间的交互,需要进行集成测试。


压力测试:为了验证系统在高负载情况下的表现,可以设计了压力测试。例如在本地模拟了大量用户和关系的数据,通过JVM监控工具观察内存和CPU的使用情况。重点测试了在大量关系和频繁修改情况下,系统的响应时间和稳定性。


回归测试:回归测试用于验证修改后的代码没有引入新的错误。在每次提交新的代码后,运行之前的所有测试用例,确保新功能的添加或Bug的修复不会破坏现有功能。


随机数据生成:通过编写随机数据生成器,模拟实际使用中的各种情况。


边界数据测试:设计极端情况的数据,例如最大和最小值,以验证系统的稳定性,这个在第二次作业中由于我的懒惰疏忽了这种测试数据的引入,导致compare改写出错。


预定义数据集:手动编写一些特定场景下的数据,确保特殊情况下系统能正确处理。

Junit测试

在设计JUnit测试时,充分利用JML规格信息。通过对JML的详细解读,确定每个方法的前置条件和后置条件,编写相应的测试用例。例如,在测试deleteColdEmoji方法时,在测试代码调用完这个方法后,重点验证所有的后置条件是否满足,而不可只关注返回值是否正确。

也即是说,Junit测试部分的思维逻辑与工程代码部分的思维逻辑应当是不同的,需要严格对照前置与后置条件。

在构造数据时,需要覆盖各种类型的数据,采用随机、边界、预定义三种构造数据策略相结合的方式。

 

架构设计

本单元学习的是JML与测试, 我们要写的代码是模拟社交关系, 由于JML基本已经把基本架构显示出来了, 所以我的架构设计并没有进行大的修改, 只是在此基础上为了性能优化及其实现, 增添了一些类和方法. 下面我准备从功能上阐述我的架构.

1. 图的构建
其实JML已经写出了图的大致架构, 我的代码实现了这种架构.
图大致采用邻接表的数据结构存储, MyNetwork类用来存储"链表头", 用一个容器HashMap来存储链表头, MyPerson类存储"链表", 用一个容器HashMap来存储链表, 至于为什么要用HashMap来存储, 是因为代码中出现了很多根据id查询的操作, 用HashMap可以大幅度优化

2. 计数器实现
本单元代码引入了异常, 异常需要记录每个id发生异常的次数, 这里新建一个类Count来管理计数是一个比较好的选择. 由于这里也需要大量通过id查询次数的操作, 所以要用HashMap来存储计数, 当某个id发生异常时, 对应修改HashMap即可

3. 计算类方法算法实现——规格与实现分离
本单元是一个比较追求性能的单元, 对于一些计算类方法, 如果严格按照JML所写, 时间复杂度较高, 很有可能超时. 规格只是说明了方法的要求和功能, 对于具体的实现并未约束, 于是我们可以规格与实现分离, 我们设计复杂度更低的算法来实现.

 

第一次作业分析

任务简析

实现用户类Person、社交网络类Network和四个异常类,重点在于按照JML的规格实现方法。

如果直接按照JML来翻译,不优化算法的话,大概率会在互测阶段被hack掉,所以后文依托于方法的实现来介绍本人的设计。

具体实现
第一次作业的方法可以分为两类:创建形(add、modify等)、查询形(query等),本人的主要策略是动态维护network中的各种关系,使得查询时尽可能是 o(1) 的复杂度,这就会增加时间成本以及内存占用,需要注意不要堆溢出。

Person
在network中的persons我使用HashMap来维护,HashMap在哈希冲突不严重的时候是靠链表来处理冲突,严重之后会自动转为红黑树,在我看来person的查询可以控制在 o(logn) 是可以接受的。故MyPerson类中的acquaintance与value容器也是使用HashMap来维护。

Relation
最后三个查询性质的函数都与person之间的关系有关:isCircle 、queryBlockSum 、queryTripleSum,从图论的角度来翻译:isCircle 是两个点是否具有连通性;queryBlockSum 是图中极大连通子图(连通分量)的个数;queryTripleSum 是图中二度正则子图的个数。

针对第一次作业情况,使用并查集是一种不错的选择,其中需要注意的点包括路径压缩按秩合并。以下为并查集:

public void union(int x, int y)
    {
        int rx = find(x);
        int ry = find(y);
        if (rx != ry)
        {
            if (rank.get(rx) > rank.get(ry))
            {
                parent.replace(ry, rx);
            } else if (rank.get(rx) < rank.get(ry))
            {
                parent.replace(rx, ry);
            } else {
                parent.replace(ry, rx);
                rank.replace(rx, rank.get(rx) + 1);
            }
            count--;
        }
    }

再次,并查集不支持删边操作,因此在进行delete系列操作时需要对并查集进行重建。由于本单元对性能要求并不高,此处没有选择对子图进行重建,而是选择朴素重建。以下为朴素重建并查集:

public void reBuild()
    {
        parent.clear();
        rank.clear();
        count = 0;
        for (Map.Entry<Integer, MyPerson> entry : persons.entrySet())
        {
            parent.put(entry.getKey(), entry.getKey());
            rank.put(entry.getKey(), 0);
            count++;
        }
        for (Map.Entry<Integer, MyPerson> entry1 : persons.entrySet())
        {
            for (Map.Entry<Integer, MyPerson> entry2 : persons.entrySet())
            {
                if (entry1.getKey() != entry2.getKey()
                        && entry1.getValue().isLinked(entry2.getValue()))
                {
                    union(entry1.getKey(), entry2.getKey());
                }
            }
        }
    }

 

第二次作业分析

任务简析
这次迭代增加了标签类MyTag,同时针对value这一属性增加了关系之间的查询

故我将新增的需要实现的方法分两类:

addTag、delTag、addPersonToTag、delPersonFromTag、queryTagValueSum、queryTagAgeVar
queryBestAcquaintance、queryCoupleSum、queryShortestPath
可以看出,第一类都是和Tag有关,而第二类更多的是依托于Acquaintance这一Person的属性

具体实现
本次作业可以讲的点不多,强测测试点的测试情况大部分取决于第一次作业的算法复杂度。

值得一提的是,公测阶段没有测出代码中一处除数为0的情况,导致强测被狠狠制裁……

 

第三次作业分析
任务简析
实现Message有关的操作,Message具有三个继承于他的子类,Message存在于network和person中

总体来感知第三次迭代的难度很低,相比于第一次作业的并查集、第二次作业很多动态维护的处理、第三次迭代并未有值得提及的设计,基本都是朴素的翻译JML实现。

具体实现
还有bug没修完,等全部修完再更新这部分。

规格与实现相分离

这一点在第一次作业中体现最明显,也即是并查集与最短路算法的使用。

同时,整个单元所采用的数据存储结构也贯彻了这一点。不同于JML规格中笼统的数组形式描述,实际工程代码中会采用HashMap\HashSet\LinkedList等java数据存储结构进行实现,以使程序达到较高性能。

那我们可以说JML规格所采用的数组形式描述是没有必要的吗?

不建议这样理解。

面对同样一份规格,每一个程序员的实现想法在细节上一定是有区分的,这就好比人与人长相或许相似,但必然存在不同。而设计与测试人员需要面对的是一份程序模板,一个人体模型,它符合我们对人体的定义——有四肢,有五官,有六腑。

规格类似于人体功能,而实现类似于高矮胖瘦。

单元体会与总结

本单元锻炼了我做测试的能力, 学习了参数化测试这一重要技能. 我也了解了JML等规格语言, 知道了已知规格该如何写代码, 如何做测试. 本单元还让我温习了很多算法内容, 尤其是图论方面的.

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

301

社区成员

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

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