OO第三单元:JML总结

周子皓-22371187 学生 2024-05-15 21:14:36

1. 分析本单元测试过程

黑箱测试

黑箱测试也称为功能测试,是一种在不了解内部实现的情况下,对软件系统的功能进行测试的方法。测试者根据软件的功能需求规格说明书来设计测试用例,重点在于验证系统是否按照预期输出正确的结果。主要特点包括:

  • 不需要了解代码的内部结构和实现细节。
  • 测试目标是检查系统的功能是否符合需求。
  • 常用的测试方法包括等价类划分、边界值分析、决策表、状态转换等。
    我在测试中主要使用了等价类划分和边界值分析。
  • 等价类划分是一种将输入划分成若干等价类的办法,每个类中的数据被认为是等效的,也就是说将输入分类。以我们的测试为例,在第二单元的测试中,为了对qtvs进行压力测试,我们可以找到会改变tag中人的value的指令,然后再找到未知tag改变的类。也就是armr,在这两条指令中,如果没有维护其他的数据结构进行存储的话,我们没办法知道究竟是哪一个tag会被修改。因此先进行这两条指令操作,然后查询,重复多次。这也是课程组其中一条数据的实现思路。
  • 边界值分析则通过将输入的数据尽量靠近边界来测试软件在这方面是否会产生错误。还是以第二次作业的测试为例,很多同学出现了两个id相减爆int的错误,这就是因为课程组在id上大量使用了边界值导致的。

白箱测试

白箱测试也称为结构测试,是一种在了解软件内部实现的情况下,对软件代码进行测试的方法。测试者需要了解代码的逻辑结构,以设计测试用例并执行覆盖测试,确保代码的各个部分都经过测试。主要特点包括:

  • 需要对代码的内部结构有深入了解。
  • 重点在于代码的逻辑覆盖,如语句覆盖、分支覆盖、路径覆盖等。
  • 主要目标是检测代码中的错误、漏洞和不合理之处。
    mr为例,在mr里面,我们为了实现动态维护,塞进去了很多东西,在已知使用动态维护这种构造的情况下。我们就可以对mr进行压力测试,一直修改边,来使得程序超时。当然,这种超时是可以避免的,在mr中,复杂度最高的就是删除后对并查集的重建,此时我们只需要置一个脏位,在不使用并查集查询的情况下不进行重建即可。但是如果代码中没有实现这部分,恰巧又被其他同学发现了,那互测可能就要吃点苦头咯。这个其他同学发现并进行针对性攻击的行为就是白箱测试。

个人认为白箱测试肯定比黑箱测试更强,更容易发现问题,毕竟有更好的针对性,黑箱测试仅仅照着规格进行设计,就算是进行较好的分析,猜测一些可能的实现,可能有时对一些不容易发现的细小错误还是没办法覆盖。

单元测试(Unit Testing)

单元测试是对软件中的最小可测试单元(通常是一个函数或方法)进行测试的方法。其主要目的是验证每个单元是否在单独执行时能正确地工作。

  • 通过隔离代码的每个单元,可以更容易地找到错误。
  • 通常由开发人员编写和执行。
  • 常用的工具包括JUnit(Java)、pytest(Python)等。

功能测试(Functional Testing)

功能测试是验证系统的功能是否符合需求规格说明书的方法。测试者不关心系统内部的实现,只关注系统是否实现了预期的功能。

  • 主要测试系统的输入和输出。
  • 包括用户接口测试、API测试等。
  • 既可以是手动测试,也可以是自动化测试。

集成测试(Integration Testing)

集成测试是在各个单元模块通过单元测试后,将它们集成在一起进行测试的方法。其主要目的是检测模块之间的接口和交互是否正确。

  • 重点在于测试模块间的接口和交互。
  • 常见的方法包括大爆炸式集成、小步集成、增量集成等。

压力测试(Stress Testing)

压力测试是通过施加超出系统正常工作负荷的压力,来确定系统的稳定性和可靠性的方法。其主要目的是找出系统的性能瓶颈和最大负载能力。

  • 模拟高并发、高负载等场景。
  • 检查系统在极限条件下的表现。

回归测试(Regression Testing)

回归测试是在软件发生改动后,重新进行的测试,以确保改动没有引入新的错误或导致其他功能失效。

  • 主要目标是验证改动的正确性和稳定性。
  • 可以是全部回归测试,也可以是选择性回归测试。

我们进行的JUnit就是单元测试,如上面定义所说,单元测试可以很好的检查软件中单个方法是否有问题。比起集成测试,单元测试复杂度更小,发现问题之后进行debug也更加容易。毕竟集成测试的时候,我们大多数都是先看输出,输出那条指令出错,未必是本条指令的问题,可能需要在单步调试的时候一点点追溯,如果数据量又很大的情况下,定位bug往往就要花掉绝大部分时间。当然,集成测试比单元测试进行起来更快捷方便也是毋庸置疑的。
功能测试也就是我们测试输出是否正确,系统行为是否正确。压力测试主要针对系统的时间复杂度进行攻击。回归测试是每次迭代后的测试,测试既要兼顾之前的指令,更要侧重新加的指令,以及两种指令之间的交互。

生成数据的策略

我们可以根据各种构造进行白箱测试生成数据。
例如在第一单元,比较容易TLE的是qtsmr
先说qts,针对可能有人使用O(3)的算法或者O(1.5)的算法直接计算qts的情况,我们可以一直输入qts进行攻击,又想到对方很可能置脏位来避免无谓的修改。那么我们可以在qts之间插入mr来修改,尽量最大化qts的有效数量,进行压力测试。
再针对使用mr动态维护的同学,也是进行全修改的mr测试,来攻击不通过置脏位实现懒修改的实现。如果他实现了懒修改,我们仍然是在qts之间插入mr,来最大化有效测试数量。
在第二单元,主要就是针对qtvsmrar。思路和上面类似,就是课程组所使用的穿插指令进行测试。还有通过临界的id进行边界测试也是很容易hack到的办法。
在第三单元,如果dce方法不进行额外处理的话会出现O(n^2)的复杂度,因此也可以作为攻击的对象。

2. 本单元架构设计

本单元主要目标是维护一个社交网络。对于人与人之间要求实现关系的增添,查询,删除等功能。实现tag类,对于tag和人,要求实现人和tag关系的增添查询,删除,以及计算tag中人属性总和。最后实现message类,对于一系列message类要求实现添加消息,发送消息,删除冷门表情等操作。

  • 对于第一次作业,我们维护一个并查集,来实现快速地关系查询。同时对并查集进行路径压缩和加权标记法来优化时间。然后再建立一个图的邻接表形式,便于dfs和bfs算法的实现,他俩分别用于删除后的重建和最短路径的查找。至于三元组,动态维护是最快最妥当的办法,我们可以在每次增添和删除关系的时候进行动态维护三元组的数量。至于延迟重建,我们则可以用邻接表记录删除,等到下一次查询的时候全体重建。
  • 对于第二次作业,我们在每个tag里动态维护要求的值,并在MyTag中维护一个private ArrayList<Pair> addRelationList;来保存期间被修改的值,借此,在需要进行重算的时候我们就可以直接对修改的关系进行遍历来维护。
  • 对于第三次作业,我认为复杂度主要在dec上,所以我选择维护一个private HashMap<Integer, ArrayList<Integer>> emojiIdToId,来实现emojiId到id的查询,可以更快从messages中删掉表情。

3. 本次出现的性能问题

本次出现的性能问题主要是在没有动态维护和没有实现懒修改上,只要对每种具有高时间复杂度的方法都实现他们,就不会出现性能问题,实现方式主要就是空间换时间,使用更多更复杂的数据结构来记录信息。规格和设计实际上是高度分离的,尤其是在一些复杂的方法上,因为规格只是表达最终要达到什么效果,并没有规定为了达到这个效果该怎么做,规格的表述往往都是最简单易懂的,我们需要做的就是在理解规格的目标之后,构造合适的算法来实现对应的规格。

4. 本单元学习体会

本单元主要学习了JML的阅读,以及根据JML规格实现代码的能力,同时训练了我们的测试能力。但是个人觉得主要的收获还是在算法层面,本单元依托于社交网络的背景,课程组引导我们学习了动态维护和懒修改的思想,学习了并查集这种数据结构及其优化,顺便带我们复习了堆的实现和应用,以及dfs和bfs等图算法。算是一个对于图算法比较全面的复习和学习了。当然,JML的学习和测试的学习,也会潜移默化的为我们未来的工作打下基础。

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

302

社区成员

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

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