BUAA_OO_Unit3总结

目录

本单元的测试过程

对各类测试的理解
  • 黑箱测试
    黑箱测试是一种软件测试方法,其中测试人员在不考虑程序内部逻辑和结构的情况下,仅通过程序的输入和输出来测试其功能。换句话说,测试人员将软件视为一个“黑箱”,只关注其输入和输出是否符合预期,而不关心其内部实现细节。黑箱测试简单直观,但可能遗漏内部错误,测试效率较低。
  • 白箱测试
    白箱测试也是一种软件测试方法,其中测试人员具有对程序内部逻辑、结构和实现细节的完整知识。白箱测试的目标是确保程序内部的所有逻辑路径都按照预期进行,并且所有的代码都被测试过。测试的目标是确保程序的所有逻辑路径都被测试过,这通常通过逻辑覆盖(如语句覆盖、判定覆盖、条件覆盖、路径覆盖等)来实现。与黑箱测试相比,白箱测试具有更高的覆盖率,可以发现源码内部错误,但设计和执行白箱测试用例通常需要更多的时间和资源。
  • 单元测试
    单元测试是软件开发中针对程序模块(单元)来进行正确性检验的测试工作。其目的在于检验程序基本组成单位的正确性。通常,程序单元是比完整的程序小的、可以独立进行编译和测试的部件。在面向过程的编程中,一个单元通常是程序中的一个函数或子过程;在面向对象编程中,一个单元可能是一个方法、类或模块。单元测试应该在开发过程中尽早进行,以便尽早发现和修复问题。单元测试有助于提高代码质量、提高可维护性。
    本单元作业中,Junit部分的作业要求都是针对某一个方法进行的。在确保测试过程中用到的其他方法正确的前提下,这种针对某一个方法进行的测试可以理解为单元测试。
  • 功能测试
    是一种黑箱测试方法,它主要关注于验证软件系统的功能是否按照需求规格说明书或用户手册的要求正确实现。功能测试是确保软件系统满足用户需求和预期行为的关键步骤。
  • 集成测试
    其目的是验证不同的软件模块、组件或子系统在集成后是否能够按照设计要求协同工作,并达到预期的行为和结果。在集成测试阶段,通常将已测试的模块按照设计要求组合在一起,作为一个整体进行测试。集成测试是确保软件系统各个部分能够正确协同工作的重要阶段。通过集成测试,可以及时发现和解决模块之间的接口问题、数据传递错误和功能调用异常等问题,从而提高软件系统的整体质量和可靠性。此外,集成测试还可以为后续的系统测试和验收测试提供有力的支持。
  • 压力测试
    压力测试用于评估系统或组件在面临极端或超出正常操作负载时的性能和可靠性。它旨在模拟实际应用中可能出现的各种高负载情况,以检测系统的稳定性、可扩展性和容错能力。
    例如,在本单元作业中,某些查询方法可能有较高的时间复杂度,我们可以构建一个接近数据限制边界的复杂图,然后反复调整图并调用相应的查询方法,来测试极端情况下程序运行时间是否符合要求。
  • 回归测试
    回归测试用于验证在软件开发生命周期中,当对软件进行修改或添加新功能后,之前的功能是否仍然按预期工作,且没有引入新的错误。回归测试的目标是确保软件的修改没有破坏已有的功能,保持软件的稳定性和可靠性。
    比如,在完成新一次作业后,我们可以用上一次作业的评测机测试新作业,以保证原有功能仍然正确。

Unit3数据构造策略

我结合使用了黑箱测试和白箱测试。在清楚代码结构的基础上,我会先构造覆盖了某些关键逻辑分支的测试用例进行测试,避免程序出现大的纰漏。然后,通过随机生成数据的方式进行黑箱测试,以保证程序在一般情况下的正确性。通过增加随机生成数据的强度,也可以对程序进行压力测试。

架构设计梳理,图的构建与维护

架构设计梳理

本单元代码架构由课程组通过JML设计约束,我未做任何创新,所以不再赘述。

图的构建与维护

社交网络图中的Person就是图的节点,通过getPerson()可以向图中添加节点。Person之间通过addRelation()添加带权重的边,这样就构建了一个图。通过modifyRelation()等方法可以改变图的结构。
本单元中,“图的维护”主要体现在修改图的过程中对TripleSum、BlockSum、CoupleSum等Network属性的维护,对BestAcquaintance等可以理解为Person属性的数据的维护,以及在修改Tag的过程中对TagValueSum、TagAgeVar等Tag属性的维护。
维护的基本原则:先确定可能改变被维护属性的方法,再在这些方法中进行相应的维护,必要时可以在类中引入新的属性。下面以BlockSum的维护为例进行分析。

BlockSum的维护
  • BlockSum的含义为:图中连通子集的数目;
  • 可能改变BlockSum的方法:addPerson(),addRelation()、modifyRelation();
  • addPerson()每增加一个人,图便增加了一个节点,该节点成为一个新的连通子集,故BlockSum++
    addRelation()为id1和id2对应的Person添加权重为value的边,即,在图的两个节点间加了一条边。如果这两个点原来就在一个连通子集中(isCircle(id1, id2) == true),则加边后BlockSum不变;如果这两个点原来不在一个连通子集中(isCircle(id1, id2) == false),则加边后两点原来各自所在的连通子集变为一个连通子集,BlockSum--
    modifyRelation()会改变id1和id2对应的Person之间的value,即,改变图中两个相连节点的边的权重值,当改变后的权重值为非正值时,删除这条边。如果modifyRelation()后,这两个点不在一个连通子集中了(isCircle(id1, id2) == false),说明这两个点原来所在的那个连通子集分成了两个连通子集,则BlockSum++;否则BlockSum不变。

作业中出现的性能问题及修复情况

在本单元第二次作业的强测中,由于ValueSum维护时出现了不必要的遍历,导致运行时间过长,CPU超时。
修改前的维护逻辑:

  1. 改变Person之间的关系权重时,ValueSum += 2 * NewValue - 2 * OldValue;
  2. 将Person加入到Tag或从Tag中删除时,遍历整个Tag,计算新的ValueSum;

但显然,将Person加入到Tag或从Tag中删除时没必要遍历整个Tag,只需遍历该Person的acquaintances,如果某个acquaintance也在该Tag中,则:
if(加入) -> ValueSum += 2 * AcqValue;
if(删除) -> ValueSum -= 2 * AcqValue

对规格与实现分离的理解

在我的理解中,JML规格主要作用为:通过JML语言严谨地规范各种属性的特点,约束各个方法的使用条件、效果和副作用。所以规格在某种程度上和接口很像,都没有规定具体的实现形式,给了编程人员一定的自由发挥的空间。也正是由于规格的这一特征,我们可以在保证程序满足规格要求的前提下提高其运行效率,避免超时情况发生。
为了分离规格与实现,我们首先要理解JML语言描述的规格,即,将JML语言转化为自然语言,然后,在自然语言的基础上设计数据结构与算法,实现代码。

规格信息与Junit测试

当代码实现可以满足规格要求时,我们认为代码实现是正确的。所以利用Junit测试代码时,我们只需要测试代码是否满足了JML规格中的assignable约束、ensures约束、requires与signals约束。逐个对照着写即可,例如:

public void testDeleteColdEmoji() throws AcquaintanceNotFoundException, PersonIdNotFoundException {
        ...
        /*@ ensures (\forall int i; 0 <= i && i < \old(emojiIdList.length);
          @          (\old(emojiHeatList[i] >= limit) ==>
          @          (\exists int j; 0 <= j && j < emojiIdList.length; emojiIdList[j] == \old(emojiIdList[i]))));
          */
        for (int i = 0; i < emojiHeatList.length; i++) {
            if (emojiHeatList[i] >= limit) {
                boolean f1 = false;
                for (int j = 0; j < newEmojiHeatList.length; j++) {
                    if (newEmojiHeatList[j] == emojiHeatList[i] && newEmojiIdList[j] == emojiIdList[i]) {
                        f1 = true;
                        break;
                    }
                }
                assertEquals(true, f1);
            }
        }
        /*@ ensures (\forall int i; 0 <= i && i < emojiIdList.length;
          @          (\exists int j; 0 <= j && j < \old(emojiIdList.length);
          @          emojiIdList[i] == \old(emojiIdList[j]) && emojiHeatList[i] == \old(emojiHeatList[j])));
          */
        for (int i = 0; i < newEmojiHeatList.length; i++) {
            boolean f2 = false;
            for (int j = 0; j < emojiHeatList.length; j++) {
                if (emojiHeatList[j] == newEmojiHeatList[i] && emojiIdList[j] == newEmojiIdList[i]) {
                    f2 = true;
                }
            }
            assertEquals(true, f2);
        }
        /*@ ensures (\forall int i; 0 <= i && i < \old(messages.length);
          @          (\old(messages[i]) instanceof EmojiMessage &&
          @           containsEmojiId(\old(((EmojiMessage)messages[i]).getEmojiId()))  ==> \not_assigned(\old(messages[i])) &&
          @           (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i])))));
          */
        for (int i = 0; i < messages1.length; i++) {
            if ((messages1[i] instanceof EmojiMessage)
                    && network1.containsEmojiId(((EmojiMessage) messages1[i]).getEmojiId())) {
                boolean f3 = false;
                for (int j = 0; j < newMessages1.length; j++) {
                    if (newMessages1[j].equals(messages1[i])) {
                        f3 = true;
                        break;
                    }
                }
                assertEquals(true, f3);
            }
        }
        ...
    }

Unit3学习体会

  • 有了清晰明确的架构和规格后,写代码会变得很简单。在今后写新的代码项目时,我会借鉴本单元的设计,先明确代码规格 (用自然语言)
  • 单元测试方便快捷,用心实现的单元测试实际上起到了评测机的作用,有利于debug,提高代码质量;
  • 知识在于运用,常用才能常熟。由于许久没用到图的相关算法,导致在写BFS时都要百度,惭愧惭愧啊;
  • 相较于自然语言,JML似乎更利于严谨地描述一个规格,能有效避免歧义。但实际上,人的思维是用自然语言描述的,我们理解JML的过程就是将其转换为自然语言的过程,这就意味着JML能描述的东西,自然语言也一定可以描述。从这个角度来看,使用JML似乎让我们经历了A->B->A这样一个非必要的过程。且考虑到JML语言的复杂性,在描述复杂规格时,往往会占据较大篇幅,这给理解也造成了一定困难,而自然语言就可以避免这一点(参考sendMessage()方法)。所以就我个人而言,JML语言是否必要还有待商榷。不过不论如何,初步学习了JML后,我意识到了自己以往在描述方法时是不够完备的。
  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值