BUAA OO 第三单元博客总结

林宇浩-22373179 学生 2024-05-18 16:23:18

BUAA OO 第三单元博客总结

测试过程

黑箱测试与白箱测试

  1. 黑箱测试是一种基于软件外部功能和需求规格的测试方法,测试人员无需了解软件内部实现的具体细节,而是将软件整体看作一个黑盒子,只关注输入和输出之间的关系,即整体的功能是否能够正确完全。
    测试人员通过输入一系列的数据和操作,然后检查系统的输出是否符合预期的结果,而不关心系统内部的逻辑和结构。黑箱测试着重于验证软件的功能性、性能、用户界面等方面,以确保软件在用户使用时能够正确地工作。
  2. 白箱测试是一种基于软件内部结构、设计和代码的测试方法,测试人员需要了解软件的内部实现细节。
    测试人员通过检查软件的代码、逻辑路径和数据结构来设计测试用例,并确保每个代码路径都得到了覆盖,以发现潜在的错误。白箱测试着重于验证软件的内部逻辑、代码质量、安全性等方面,以确保软件在各种条件下都能够正确运行和维护。
  3. 在本单元的作业中,黑箱测试相当于中测(不包括junit测试)以及强测,对整体代码的功能和性能进行测试,只关注输入和输出是否符合要求。而白箱测试相当于中测的junit测试,针对每次作业中的某一个方法进行测试,注重的是该方法是否完全符合JML规格的要求,并且在符合规格要求的情况下,不修改其他内容。

    单元测试、功能测试、集成测试、压力测试、回归测试

  4. 单元测试:
    单元测试是针对软件中最小的可测试单元进行的测试,通常的测试对象是函数、方法或类等独立的代码片段。单元测试的目的是验证每个单元的行为是否符合预期(JML规格),通过编写测试用例来检查单元的输入输出是否正确,以确保单元在各种情况下都能够正常工作。单元测试通常是用来发现和修复代码中的错误,提高代码的质量和稳定性。本单元的单元测试即是junit测试,测试对象是作业中的某个方法(queryCoupleSum、deleteColdEmoji),检测其行为是否JML规格。
  5. 功能测试:
    功能测试是验证软件的功能是否符合需求规格和用户期望的测试方法。功能测试通过模拟用户的操作行为,测试软件的各项功能是否能够正常工作。功能测试通常由测试团队执行,覆盖软件的各个功能模块,以确保软件在用户使用时能够按照预期工作。本单元中功能测试相当于每次测试只针对某个功能,关注其能否实现自己部分的功能要求。
  6. 集成测试:
    集成测试是将已经经过单元测试的各个单元组合在一起进行测试,验证它们在集成后是否能够协同工作。集成测试的目的是检查各个单元之间的接口和交互是否正确,确保软件的各个组成部分能够无缝集成并协同工作。在本单元中集成测试类似于中测(不包括junit测试)以及强测,对整体代码的功能和性能进行测试。
  7. 压力测试
    压力测试是评估软件在不同极限邻接条件下的性能表现和稳定性的测试方法。压力测试通过模拟大量用户访问、大数据量、重复性高压数据输入等场景,测试软件在高负载情况下的性能表现和响应速度,以及在持续压力下是否能够稳定运行。压力测试可以帮助发现系统在负载增加时可能出现的性能瓶颈和问题,从而优化系统的性能和稳定性。在本单元中,强测有着若干个点对于作业中的方法都进行了压力测试,测试对该方法是否进行优化或者维护。
  8. 回归测试:
    回归测试是在软件进行修改或更新后重新运行之前已经通过的测试用例,以确保修改或更新没有引入新的错误或破坏已有的功能。回归测试的目的是验证软件的变更是否对原有功能产生了负面影响,以保证软件的稳定性和兼容性。

数据构造策略

  1. 首先是数据覆盖要广。本单元的作业中,涉及到了诸多的操作,我们需要保证在生成数据的时候,要覆盖到每一种操作,避免遗漏
  2. 其次是数据的强度。在保证数据覆盖到所有操作的前提下,对数据量进行增加,例如:增加至10000条指令等。
  3. 接着可以测试临界数据。可以对于person的id进行边界数据的测试、对于money也可以进行临界数据测试。
  4. 最后是压力测试。压力测试mr、qvs方法,反复调用这些方法,观察是否会出现tle的问题。

架构设计

在本单元的迭代作业中,我们一步一步构建起了一个社交网路的图模型,该社交网络中有着person、tag、message这三个主要的属性。本次主要类是MyNetwork,在其中实现person间的交流,将person、tag、message三个类联系起来

第一次作业

  1. 在MyNetwork中,使用HashMap<Integer,Person> persons来保存社交网络中的每一个节点,在查询的时候,利用personId直接查找即可。其次,使用HashMap<Integer,Integer> persons来构建并查集,方便后续判断两个节点是否在一个连通块中。用HashMap<Integer, HashSet> graph来当成一个二维数据,来保存这个图的信息,方便后续重建并查集。
  2. 维护的话则需要在每个操作之后,都判断是否会对某个属性造成影响。在ad、ar以及mr操作后,都会对graph进行改变,我们则需要对其内容进行维护。同时ar和mr也会改变并查集,我们则需要根据graph来重建并查集。

    第二次作业

    在第二次作业中,新增了tag类,需要我们为person增加tags的属性,我使用了HashMap<Integer, Tag> tags来保存该属性。
    其次,在tag类中,用属性ageSum和valueSum来保存需要返回的值,并设置dirty来进行维护,判断是否需要重新计算。

    第三次作业

    在第三次作业中,新增了message类,整体来说没有什么需要维护的变量,需要注意的是用LinkedList来存储messages变量,便于插入操作。

    性能问题及修复

    第一次作业

    性能问题

    在第一次作业中,会导致性能较差的方法主要有两个:分别是modifyRelation()中的并查集重建以及queryTripleSum()。

    优化

  3. modifyRelation()中的并查集重建。在改变两个person的relation之后,如果他们之间不再连通则需要重建并查集。在最初的时候,我们无法判断改变之后是否连通,所以我们只要有改变,就进行并查集重建。而在思考后,发现tripleSum是否改变可以用来判断改变之后这两个person是否连通。如果tripleSum改变,则说明这两个person仍然连通,无需重建并查集。

    在这里插入图片描述

  4. 维护queryTripleSum()
    tripleSum的计算是一个O(n^3)的复杂度,所以必须要用tripleSum这个变量将这个数存起来,避免每次都计算,这样会极大地耗费时间。如果将tripleSum存起来,则需要进行维护。这个tripleSum变量只会在addRelation和modifyRelation的时候会改变,所以我们只需要在这两个方法中对tripleSum进行维护即可。

    在这里插入图片描述


    需要注意的是,遍历的时候无需遍历所有person,只需要遍历其中一个person的acquaintance即可,这样也可以很大程度上加快维护的速度。

    第二次作业

    性能问题

    在第二次作业中,会导致性能较差的地方有三处:person的bestId,tag的方法getValueSum()以及getAgeMean()。

    优化

  5. person的bestId
    对于person的bestId,我们在person中新增一个属性bestId来保存这个数据,在需要的时候直接返回即可。当addRelation和modifyRelation的时候bestId会改变,我们需要对其进行维护。

    在这里插入图片描述

  6. tag的方法getValueSum()
    tag的方法getValueSum()是计算tag属性persons中每一个person的value的总和,对于这个总和我们需要将其存起来,方便查询,避免总和不变的时候重复计算。可能改变valueSum的操作有:addRelation、modifyRelation、 addPersonToTag以及deletePersonFromTag。所以我们只需要在这四种操作下对valueSum进行维护即可实现valueSum直接返回。
  7. getAgeMean()
    tag中计算年龄平均值的时候,提前将ageSum存起来,这样不需要每次重复计算,在addPersonhedeletePerson的时候维护ageSum即可。在计算年龄的平均值的时候,直接返回ageSum/persons.size()即可。

    第三次作业

    第三次作业整体来说没有时间复杂度较高的方法,主要是对messages的操作有一个是将数据插入其开头,所以我们选择LinkedList来存储messages,这样插入操作的时间复杂度就变成O(1)。

规格与实现分离

规格与实现分离是软件开发中的一种设计原则,根本目的是将软件系统的规格与实现相互独立,这样有利于提高软件的可维护性、可扩展性和可重用性。

  1. 规格指的是软件系统的功能需求、设计约束、接口规范等描述性文档,规定了其应该实现什么功能,不应该做些什么。主要包括:用户需求文档、功能规约、接口定义、数据模型等。
  2. 实现指的是根据规格描述来编写软件代码、设计系统架构、实现功能逻辑等具体工作,是将规格转化为可执行软件的过程。在实现规格的过程中,还应考虑软件的性能、可维护性、安全性等,确保软件在实际应用中能够运行地稳定、高效。
  3. 实现了规格与实现分离原则,首先使需求定义变得更加清晰,有助于开发团队理解用户需求,减少沟通误差。其次可以进行独立的开发与测试,测试人员可以根据规格独立进行测试,提高开发效率和测试覆盖率。最后使易于维护和扩展,使得开发人员可以根据规格准确地进行变更,而不影响整体系统的稳定性。

Junit测试

  1. 首先关注JML规格要求的前置条件、以及后置条件,在junit里保证前置后置条件都能满足。然后主要关注点就是pure,及不对哪些变量内容进行改变,我们在junit测试中就要关注前后是否会改变。如果改变则不正确。
  2. 其次是,利用较大的数据和临界数据去测试,这样可以避免一些特殊情况的发生。

    学习体会

  3. 学习JML规格后,我深刻体会到规范化的编程思维对于软件开发的重要性。JML规格语言能够帮助程序员清晰地定义程序的行为和约束条件,从而提高代码的可读性和可维护性。通过规格化的方式,我们可以更准确地理解程序的功能和预期行为,减少程序中的错误和漏洞。在编写代码之前先定义规格,然后编写测试用例来验证程序是否符合规格,这种方法可以帮助我们更好地设计代码结构,提前发现和解决潜在的问题。
  4. 其次是,本单元作业涉及到了社交网的图模型,并且在性能上有着一定的要求。所以,了解学习了并查集的相关知识,并将其运用在本单元作业中。同时也学习了bfs、dfs、路径压缩和按秩合并等算法,极大地优化了作业的性能
  5. 最后是,在作业中我们要适当的考虑空间和时间的取舍。本单元作业对时间的要求较为严格,所以我们需要在查询操作里,尽可能地将要查询的结果保存起来并对其进行维护,这样在查询的时候可以直接返回需要的结果,避免了重复性计算。这样的思维是空间换时间的思维,我们需要在追求速度性能而不注重空间的时候,适当采用这种思维。
...全文
88 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

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

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