BUAA_OO_第三单元总结

柔光_ 柔光_ 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区

分析本单元的测试过程

  • 白箱测试的概念

    白箱测试,也称为白盒测试、结构测试、透明盒测试或逻辑驱动测试,是一种基于应用程序内部结构和设计的测试方法。测试人员可以查看源代码,了解程序的内部结构、设计和逻辑,从
    而设计测试用例。白箱测试旨在检查软件应用程序的内部结构、代码和数据流程,强调对程序的逻辑覆盖率的检查,以确保所有代码路径都被测试到。

  • 黑箱测试的概念

    也称为黑盒测试、功能测试或数据驱动测试,是一种基于软件需求和功能规格说明的测试方法。在测试中,测试人员将程序视为一个不能打开的黑盒子,完全不考虑程序内部结构和内部特
    性,只关注程序接口。黑箱测试通过输入数据并观察输出数据来检查程序功能是否按照需求规格说明书的规定正常使用,是否能够正确地接收输入数据并产生正确的输出结果。

在日常编写代码的过程中,我对代码进行得最多的是黑箱测试,也就是设计针对不同功能的输入,比较生成的数据与预期数据是否一样,包括我们使用的测评机也是如此。而在本单元,以及上
学期为代码的方法写的junit测试就是白箱测试,在函数内部的关键地方设计assert语句,时刻判断每一步是否正确执行,虽然最后当然也会检查输出的正确性,但这种测试方法还检验了方
法有没有按照我们预期的方式执行,有没有产生我们预期之外的副作用。上学期面向对象课程先导会检查junit代码的覆盖率、分支覆盖率等,就是为了增加代码可靠性。

  • 单元测试
    主要是指对最小单元进行检查和验证。“最小单元”可能是一个方法、一个过程、一个函数或者一个模块。在本单元中,实现一个功能可能需要调用多个方法。比如我在实现qcs时,采用在net
    work里,会调用person类里的getBestId()方法,如下图:

    img

    而在完成代码进行黑箱测评时,当发现qcs出错,就难以确定是哪一个方法出现了问题,虽然这样的设计思路有所违背“高聚合、低耦合”的思想,但是在实际代码实现中调用多个方法实现一
    个功能还是常见的。所以进行单元测试是很有必要的,我的方法是构造针对单一方法的数据进行对拍,从最底层、最基本的方法向上检查。

  • 功能测试
    功能测试主要是对要实现的功能有没有正确实现进行检查,与黑箱测试类似。功能测试可以穿插在各种测试方法中,比如上述提到的单元测试,当然就是要检测每一个单元有没有正确实现
    想要实现的功能。

  • 集成测试
    概念:集成测试,也称为组装测试或联合测试,是在单元测试的基础上,将所有模块按照设计要求(如结构图)组装成为子系统或系统后,进行的测试活动。它的主要目的是确保各单元组合
    在一起后能够按既定意图协作运行,并确保增量的行为正确。
    除此之外,集成测试也会对某些关键模块进行类似白箱测试的充分测试。因此集成测试是基于黑箱测试和白箱测试基础之上的。 继承测试在保证关键模块内部的具体实现细节是正确的同
    时,还会检查各个模块之间的协作是否正确。比如在第三次作业中,新增了各个种类的message,而在network中可以进行对message的增加、发送等操作,所以network类和各个message类
    具有协作关系。在进行集成测试时,可以先测试各个message方法的正确性,再进行增加message、发送message的操作,对输出进行分析。

  • 压力测试
    在压力测试中,测试人员会模拟大量用户同时访问系统的场景,以检查系统在资源有限或竞争激烈的条件下是否能够正常运行。这种测试方法可以帮助组织预测系统的瓶颈,并据此制定相应
    的扩容计划或优化措施。比如在本单元的junit中,我使用随机参数大量生成了person的id和人与人之间的关系,然后对生成的network进行大量的qcs指令测试。

  • 回归测试
    回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。在本单元的作业中,在进行bug修复或者新增了新的功能时都可能会修改原先的代码,
    这时就需要对原有指令进行测试,确保没有破坏原有代码的正常功能。

  • 数据构造策略
    在完成新的功能增加后,针对这个新的功能构造大量的数据进行测试,一次只测试一个功能,所以要从基本的功能向上进行测试。在构造数据的时候,需要考虑到一些极端情况,比如构造一
    些完全图等。除此之外,一些特殊情况难以自己想到,所以需要随机测试,即使用测评机进行测试。

分析本单元架构、实现的图模型及其维护

  • 本单元架构

    本单元实现了一个社交网络。在network中维护了此network中的person、message和emoji。在network中,可以实现增加或减少person、增加或修改person间的关系、查询某两个person间
    的连通性、增加或删除tag、为tag加人或减人、增加、发送message、存储emoji等基本功能。总的来说,network根据指令对整个社交网络进行更新,同时也接受query类指令返回network
    当前的状态。在这个社交网络中,最基础的是person的增减,然后便是person间带权的关系,也就是图的边,然后便是tag,在发送消息的时候,发送消息的person可以选择向所拥有的tag
    发送消息,也就是发送到这个tag中的每一个人。这一点有些像在微信发送朋友圈时可选择的“分组”功能。

    • 实现的图模型及其维护

      本单元中为了实现不同的功能,总体实现了两种图模型。

      • 并查集
        本单元实现的并查集主要是为了便于查询两个person之间的“连通性”。假如将person视为点,将person间的关系视为边,那么整个社交网络可以简化为一个图。那么两个person间的“连
        通性”就容易理解了。在我的实现中,我单独实现了一个类来实现并查集,这个类的实例作为属性存在于network中。

        img

        在并查集中,主要实现了两个方法,也就是find()和merge(),find方法查询某节点的父节点,而merge方法将两个节点连接在一起,具体的实现方法是查询这两个节点的父节点,假如两
        个父节点相等,说明已经连接在了一起,假如没有,就需要将其中一个父节点的父节点设为另一个父节点(就是委屈其中一个父节点当另一个的子节点),从而两个节点所在的连通图就连接在
        一起了,形成了一个更大的连通图。
        在维护方面。首先是并查集的建立,在增加节点和边,也就是增加人和人与人间关系的时候需要对并查集进行find操作和merge操作

        img

        在find方法中,如果当前并查集中还没有该id的信息,就会自动初始化并存储该id。
        在本单元中,非常具有难度的是当对关系进行修改后关系删除该怎么更新并查集。我最终选择的是直接清空并查集并重新建立。为了减少该操作的次数。我设置了一个脏位。当关系删
        除后,仅仅设置该脏位的值,到了要使用该并查集的时候,再去检查该脏位,假如需要重建就重建,并置低该脏位。
        在并查集类中,我实现了如下几个方法来实现上述操作

        img

        在network类中,在使用并查集前会检查该脏位的值

        img

      • 图
        图的建立主要是为了实现最短路径查询。
        我的实现中,图的建立非常简单,使用了一个嵌套容器:

        img

        维护该图的方式也更加简单,需要在增加人的时候新增节点并初始化与其有关系的person的容器:

        img

        增加关系的时候就向对应容器增加person

        img

        同理,在删除关系时也需要将该person中从对应容器删除

        img

作业中出现的性能问题和修复

本单元中出现的性能问题集中在第二次作业。出现了两个问题,且两个问题都是因为没有采用动态维护导致的。

  • 第一个问题出现在没有维护person类里的bestid(value最大的acquaintance)导致qcs超时。动态维护bestid时也出现了问题,因为忘记了modifyrelation时可能会将原先的bestid对应的值
    减小。
  • 第二个问题出现在qtvs用时过长,可以动态维护所需要的值,同样需要注意在改变关系时要实时动态维护所保存的值。

对规格与实现分离的理解

在本单元的学习过程中,我逐渐意识到看到规格主要就是明白一下这个方法的作用,要是看到规格是二重循环和三重循环就应该要考虑其他算法了。在本单元作业中,我绝大部分容器都是
使用Hashmap实现的,为了在查询的时候降低时间复杂度。此外,还使用了dfs、并查集等等来进一步降低算法复杂度。规格是为了严格规范我们的代码,避免自然语言二义性可能带来的误
会。所以规格的作用是精确地说明方法将产生的效果,我们在实现的时候,一方面要根据规格选择最为合适的数据结构和算法,另一方面要注意“规格让做的一定做,规格不让做的一定不做”。

Junit测试

本单元中的junit测试我根据实验代码均实现了参数化随机生成数据测试。

  • 根据规格信息来实现Junit测试
    根据规格信息写Junit测试不仅严谨,而且简明容易操作。比如假如该方法是pure方法,就要检查方法执行前后是否没有产生任何副作用,有没有对不应该改变的对象进行了修改。对于方
    法的返回值,假如不是void,就要严格按照规格,在调用方法前先计算出预期返回值,再对方法实际返回值进行对比。对于方法所要求的其他副作用,我的实现中实现了两个具有同样信息的network,
    唯一区别是其中一个调用了所要测试的方法,另一个没有,对这两个network中的信息进行对比就可以检查是否实现了规格要求的副作用。
  • Junit检验代码是否正确实现规格的效果
    我认为Junit能够很好地检验代码是否实现规格。首先是Junit可以生成大量数据对代码针对仅仅一个方法进行多轮检测,可以很好地覆盖各种情况。其次是Junit可以在Test方法各个地方设
    置assertTure方法,可以检测代码的各个方面有没有正确实现,可以严格地按照规格写Test方法,只要检验到了每一行规格,就可以正确检验代码是否正确地实现了规格

本单元学习体会

在本单元的学习中,我学习到了JML的基本语法和使用方法。同时深刻体会到了代码规格和自然语言的差异性,以及为什么要使用代码规格。代码规格可以无二义性地限制我们的代码,使得
在实际开发过程中避免一些可能产生的误会和纰漏。此外,我还对图的算法和时间复杂度的降低有了进一步的体会,也意识到了实现动态维护等方式可以大大降低代码的时间复杂度。

...全文

相关推荐

评论
收藏 已收藏
分享
返回顶部