BUAA2024OO第三单元博客

马赛龙20373127 2024-05-18 17:59:56

第三单元博客

  • 分析本单元的测试过程

    • 本单元测试的实现

      本单元测试的实现过程主要包括,1、编写符合要求的正确检验代码。2、设计边缘数据并手动构建网络。3、设计随机数据生成方式,建立随机生成的网络。4、组合为测试套件,测试多个测试点。5、设计错误函数,并检测JUnit设计能否检验出错误。

    • 谈谈你对黑箱测试、白箱测试的理解

      黑盒测试:展开黑盒测试时,测试人员不需要了解程序的内部结构、实现细节和代码。只需关注软件的功能和输入输出关系。所以黑盒测试也成为功能测试。黑箱测试更注重于从用户的角度出发,检查程序是否能够按照需求规格说明书正确运行。

      黑盒测试的优点在于不需要了解代码内部的实现方法,测试人员无需具备编程知识,只需理解软件的功能需求,依据需求规格说明书,即可通过输入数据来检查程序是否能够产生预期的输出。这可以实现开发和测试的分离,提高开发的效率。

      缺点同样在于无法了解到代码内部的实现,所以发现问题后,难以立刻确定问题的具体位置,需要开发人员进一步分析,针对具体代码进行评价。同时由于无法了解内部的实现,所以测试用例不能保证覆盖所有代码情况,可能会遗漏内部的某些特殊情况或者代码实现的错误。

      白盒测试:白盒测试也称为基于代码的测试。白盒的意思是内部结构是可以看到的,代码的运行也是可视的。测试人员需要了解程序的内部结构、实现细节和代码。测试是基于代码的逻辑和结构进行的。

      优点是测试人员可以根据代码的内容来设计样例,可以覆盖更多的执行路径或分支条件。同时可以检查代码的实现方式是否存在逻辑错误。

      缺点在于测试人员同时也需要理解技术,具备开发知识。所以测试的成本更高。而且白盒测试的行为往往与用户行为相区分,其情景与用户使用情景区别较大,无法发现用户体验问题。

    • 对单元测试、功能测试、集成测试、压力测试、回归测试的理解

      • 单元测试往往将视野局限在代码的一小部分,比如本单元作业中每次作业对某一个函数进行测试。通过单元测试可以对模块最小组成部分进行测试,根据断言来判断最小单元是否按照预期的方式运行。单元测试可以保证某个组件的行为符合要求,在开发初期可以及时发现问题,也可以缩小故障出现的位置,便于及时排查错误。

      • 功能测试是对代码的功能实现的检测。一方面,功能测试可以称为黑盒测试,在这个过程中,测试人员不需要检查内部的具体实现,只需要关注测试结果是否符合要求,即是否完成了设计的功能。另一方面,在功能测试中可以区分不同的功能需求,从而设计测试不同功能的用例,便于确定系统不同功能的差异。

      • 集成测试将各个模块组合到一起进行测试,相比于模块测试,集成测试可以检查模块间的交互问题,旨在确定模块间的合作是否正确,模块间的接口实现是否符合预期方式。其测试范围更大,更注重对项目整体的功能实现的测试。在本次作业中,建好图后测试使用多种命令进行查询也属于集成测试,从整体上大量测试其效果。

      • 压力测试旨在测试系统的负荷能力和性能瓶颈。压力测试的关注点并不集中在功能的正确与否,而是试图找到系统能承受的数据限度。所以其测试数据往往更加苛刻,数据量更大,运行时间更长,涉及大量并发等操作。

      • 回归测试是对系统进行修改(例如修复缺陷、添加新功能)后,再次进行测试,以确保新修改没有引入新的错误,且原有功能正常。在本单元的作业中,每次迭代都需要展开回归测试。一方面指导书的描述发生变化硬性要求代码的实现也进行相应的改动,另一方面基础的前置条件改变,也可能导致曾经正常的代码无法实现新条件下覆盖实现所有的内容。所以每次作业迭代后要检查是否还满足上次作业的要求。

    • 数据构造有何策略

      • 边缘数据

        对于每次作业需要检测的函数,有一些可以人为设计的极端数据点。这些数据点在进行随机测试时可能并不能保证构造出来,但是对于检测函数的完备性十分重要。例如,在第一次的作业中,查询三角形数量可以设想存在一些极端情况,包括图中无三角形,图为空,图为全连接,图中仅有一个三角形等情况。针对这些内容,人为设计数据并构造网络即可实现。

        @Test
        public void testEmptyNetwork() {
            assertEquals(0,network.queryTripleSum());
        }
        ​
        @Test
        public void testNoTriple() throws EqualPersonIdException,
            PersonIdNotFoundException, EqualRelationException {
                //
        }

        边缘数据的优点在于可以测试那些极端情况,而且在随机数据生成时不一定能够出现。缺点时数据点需要手动设计,往往需要投入更多的时间精力,且覆盖范围有限。

      • 随机数据

        随机数据的生成较为简单,设计好数据生成策略后即可批量生成大量的测试用例。大量随机数据检测可以增加测试的全面性和鲁棒性。而且可能会发现潜在的未被发掘的边缘情况,无论是检测模型在更广泛的情况下的表现和正确性还是检测极端情况都有价值。

        随机数据在生成时并不需要实现全部参数随机。根据特定的需求,可以在生成图时先选定节点数和边的个数,以及边的正负值。这种有限的随机和参数的选定可以让我们在测试的时候选定稠密图或系数图以及图的大小,根据需求灵活调整数据的规模。但在随机数据生成时,也要注意依据指导书的要求,避免生成违法数据或违法操作。

        private void generateRandomMessageData(Network network, int personCount, int messageCount)
                    throws EqualPersonIdException, EmojiIdNotFoundException, EqualMessageIdException,
                    RelationNotFoundException, TagIdNotFoundException, MessageIdNotFoundException,
                    PersonIdNotFoundException, EqualRelationException, EqualEmojiIdException {
                        //
                    }
  • 梳理本单元的架构设计,分析自己的图模型构建和维护策略

    架构设计如图所示:

     

    图模型的构建主要基于MyNetwork和MyPerson实现,其中MyNetwork储存图的全部节点,而MyPerson储存一个节点和它的邻接点。

    //MyNetwork implements Network
    private HashMap<Integer, MyPerson> persons;

    在MyNetwork中使用HashMap储存全部节点便于快速查找节点。

    //MyPerson implements Person
    private int id; //节点的唯一标识
    private HashMap<Integer, MyPerson> acquaintance;
    private HashMap<Integer, Integer> value;

    在MyPerson中存在唯一标志节点的id,储存所有邻接节点的acquaintance。value本质上是刻画两个节点的联结强度。

    在整个迭代过程中,始终保持了这样的数据结构。但由于新的需求对于代码性能提出了新的要求,所以需要使用并查集、大顶堆等数据结构辅助特定函数的实现。

    private PriorityQueue<PersonRelation> valueHeap;
    //大顶堆有助于查询最大值,在查询最好朋友时可以大幅度提高性能。
  • 分析作业中出现的性能问题及其修复情况,谈谈自己对规格与实现分离的理解:

    在作业中,实现查询三角形函数queryTripleSum()时,一开始使用了三重循环时间,但这样的时间复杂度非常高,容易导致超时。在实现最短路径查询时,也存在大量可以实现的算法。选择时间复杂度最优的方法有利于避免超时。同时,在查询最佳朋友时,如果使用普通的HashMap储存value则会导致每次查询一个成员的最佳朋友时都要花费O(n)的时间。改为使用最大顶堆则大幅提高效率。

    在JML中,JML代码描述了函数的规格,但规格并不等价于实现方式。

    规格是描述软件系统行为的高层次抽象,它定义了系统应做什么,而不是如何做。规格包括了方法的前置条件、后置条件、不变式等。

    实现是指实际编写的代码,根据规格定义的需求执行具体的操作和计算。

    规格定义了对函数的要求,或者可以称为格式化的要求,让要求尽可能地清晰明确,可以检查是否对应。而实现则要在满足规格的基础上,思考更多的问题要求。在本单元的作业中,主要是要满足时间性能的要求。所以规格并不真正规定实现的方式。这样做的好处是可以将需求的设计和实现相分离,有助于在一个大型项目中实现高效合作,并维持功能的稳定与可预期、可检测。这样一种统一的语言让整个团队中的开发、设计人员能够清晰交流。除此之外,这也有利于模块化的实现,贯彻高内聚低耦合的设计原则。

  • 本单元中同学们实现了Junit测试方法,总结分析如何利用规格信息来更好的设计实现Junit测试,以及Junit测试检验代码实现与规格的一致性的效果:

    JML 是一种用于描述 Java 类和方法的行为的规格语言。它允许我们使用特殊的注释来描述类和方法的预条件、后条件和不变性。而JUnit也提供了多种断言,可以检测代码运行前后是否符合规格要求,分为预条检测试,后条件测试,不变性测试。

  • 本单元学习体会:

    本单元我认为我了解了以下三项内容。

    一、了解了JML的基本语法和JML的用途与目的。这种设计确保了代码的精准性,避免了自然语言存在的歧义,便于程序员间的交流与设计。JML的严谨性和精确性也让我感受到其功能的强大和先进系统对代码精确性的要求。

    二、JUnit的单元测试让我初窥测试门径,在此之前我从未听说过JUnit这样的单元测试内容。JUnit可以使用多种断言检测结果,也可以把多个测试组合为测试套件进行统一检验。而且JUnit提供多种方法,可以自由定义测试方法及其生命周期。

    三、让我温习了图的数据结构和图算法实现,包括常用的算法和降低时间复杂度的方式。尽管JML已经定义了各个类的结构和行为,但JML仅仅对代码做出了结果的要求,对于内部的实现方法并没有做出限定。所以尽管是完成函数,也存在大量自由发挥的设计余地。一方面需要根据对时间复杂度和数据查询、插入操作要求的分析,设计合适的数据结构,选择HashMap,ArrayList等数据结构储存数据,另一方面需要根据JML描述的函数方法,使用更小的时间复杂度,更巧妙的算法实现。这也是一次对算法和数据结构的复习。

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

301

社区成员

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

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