您现在的位置是:首页 >学无止境 >BUAA OO Unit3——JML网站首页学无止境

BUAA OO Unit3——JML

Liuliuuuu 2024-06-17 11:28:05
简介BUAA OO Unit3——JML

一、分析本单元测试过程

1.1 黑箱测试与白箱测试

黑箱测试和白箱测试都是软件测试中常用的测试方法。在本单元的作业中对于这两种测试方式都有涉及。可以说在之前的所有编程经历中,我们最常使用的就是黑箱测试,通过输入样例数据,将输出与标准输出进行对比进而判断程序的正确性。而白箱测试在之前涉猎较少,但其在本单元中体现在了OkTest这个方法中实际上就是进行了白箱测试,通过不同的返回值来判断处理数据的程序的具体错误。

1.1.1 黑箱测试

黑箱测试是一种测试方法,测试人员只关注被测软件的输入和输出,而不关注被测软件的内部实现细节。黑箱测试是从用户的角度出发,测试人员通过输入各种数据来检查被测软件的输出是否符合预期,以此来发现软件中的错误和缺陷。黑箱测试更关注软件的功能是否符合用户需求。

1.1.2 白箱测试

白箱测试是一种测试方法,测试人员需要了解被测软件的内部实现细节,包括代码、算法、数据结构等。白箱测试是从开发人员的角度出发,测试人员通过检查代码、调试程序等方式来发现软件中的错误和缺陷。白箱测试更关注软件的内部实现是否正确。

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

1.2.1 单元测试

单元测试:对软件中的最小可测试单元进行测试,通常是对代码中的函数或方法进行测试,目的是验证代码的正确性和可靠性。

1.2.2 功能测试

功能测试:对软件的各个功能模块进行测试,以验证软件是否符合用户需求和设计要求。

1.2.3 集成测试

集成测试:将各个模块集成起来进行测试,以验证模块之间的接口和交互是否正常,同时也可以发现由于集成带来的新问题。

1.2.4 压力测试

压力测试:对软件进行负载测试,模拟多用户、高并发等场景,测试软件的性能和稳定性。

1.2.5 回归测试

回归测试:在软件发生变更后,对之前已经测试过的功能进行再次测试,以确保软件变更不会影响已有的功能。

1.3 测试工具

在本单元中我主要使用了同学通过基于对拍实现的评测机进行测试。

1.4 数据构造

本单元的作业相较前两单元而言,有着指令更多更复杂的特点,因此在构造测试数据时首先要注意的就是尽可能地覆盖所有指令,并且提高各指令间的交叉度,使数据更加复杂,这样就可以很好的考验程序处理某一指令时是否会对其他指令相关数据产生错误的影响。其次,由于本单元中实际上是对图结构相关算法进行了一定的考察,所以我们在构造数据时要尽可能提高图的复杂程度,从而可以更好的测试qbsqtsqcsqlm等指令的正确性以及运行效率。

二、架构设计

本单元实际上实现了一个简易的社交系统,我们将Person类的对象作为图的节点,每一个Person对象有如下属性:

public class MyPerson implements Person {
	// ...
    private int rootNode;
  
    private HashMap<Integer, Person> acquaintance;
    private HashMap<Integer, Integer> values;

    private int bestId;
    private int bestValue;
    // ...
}

其中的acquaintance储存着“该Person认识的人”,即图中节点之间的边,values则对应着图中每条边的权值。

在维护图的过程中,我在MyNetwork类中维护了一个二维数组matrics,每次出现add_person指令时就扩充该邻接矩阵,add_relationmodify_relation时就改变邻接矩阵中的值。在实现query_least_moments指令时,采用dijkstra算法,从而求得最短路径。

三、分析性能问题及修复情况、对规格与实现分离的理解

在本单元中主要有如下几个涉及到图的相关操作且复杂度较高的指令,如果直接采用JML表述中的实现方式,则往往会在强测时出现超时的情况。

  • query_block_sum: 该指令要求返回连通子图的数量,我在MyPerson中添加了rootNode属性,在add_relation时,将所有新的可达的节点的rootNode都设置为新的这个节点。从而使该指令复杂度为O(1)。
  • query_triple_sum: 要求返回三元环数量。在add_relationmodify_relation后,若加入了新的边或去掉了一个边,则进行一个复杂度为O(n)的判断,检测v1的所有认识的人是否认识v2,从而实现了对tripleSum的动态维护。
  • query_circle: 即判断两个节点是否连通,我们之前已经维护了rootNode,那么只要两个点的该属性相同,则可以判断这两个点是连通的。
  • query_couple_sum:该指令要求返回互为value值最大的人的组数,我在add_relationmodify_relation中进行修改,若新加的value比此人当前的bestValue大或修改的value为此人当前的bestValue,则需要判断coupleSum是否需要变更。最后只需要直接返回该属性即可,复杂度为O(1)。
  • query_least_moment:该指令是本单元中算法最难实现的指令,我使用了dijkstra算法进行最短路径的查找,如上“架构设计”中所示。

下面是我个人对于“规格与实现分离”的理解。

我查到了这样一段定义。规格与实现分离是指在软件开发过程中,将软件的需求规格和实现代码分开处理,使得两者的修改互相独立,从而提高软件的可维护性和可重用性。

那么,在规格与实现分离的设计中,首先需要将软件需求规格进行详细的分析和设计,确定好软件的功能和性能要求等。然后再将这些规格转化为具体的代码实现,通过编程语言实现功能。在这个过程中,将规格和实现代码分开处理,可以避免相互依赖和影响。这样做能够有很多益处,通过规格与实现分离的设计,可以使得软件的开发过程更加模块化和可重用,降低软件开发的风险和成本。同时,也可以提高软件的可维护性和可扩展性,使得软件能够更好地适应不断变化的需求。

正如这一部分需要先分析本单元作业的性能问题再谈对规格与实现分离的理解,假如规格与实现并未分离,那么我们在处理这些复杂度高的方法时,难免会“捉襟见肘”,会导致程序最终的运行效率降低。而脱离了规格的限制,我们才可以通过各种算法去降低复杂度,最终以较好的性能实现本单元作业。我认为规格与实现分离正是这个道理。

四、OK测试

OK测试可以帮助开发人员验证代码是否符合规格要求,并且可以及早发现潜在的问题和错误。如果代码与规格不一致,那么测试就会失败,开发人员就需要回到代码中查找问题并进行修复。这样可以确保代码与规格一致,并且可以提高代码的质量和可靠性。因此,OK测试在软件开发过程中起着至关重要的作用。

与单纯的检验输出结果与正确结果的差异然后逐步调试相比,这种测试方法可以更精确地定位bug,极大提高效率。

我认为OK测试可以帮助同学们加深理解规格并检验自己的实现,对于本单元的学习有较大帮助,可以在之后的课程里继续相关的内容。

五、学习体会

在本单元我们学习的重心是JML,通过本单元的学习首先让我理解了规格与实现之间的联系和区别,我认为关于规格的相关知识会对我们之后参与大型项目中的核心程序编写时有着重要意义,只有通过规格的限制才能让代码“有章法”,有更好的扩展性和可维护性,提高代码复用率。此外本单元的作业也加深了我对图相关算法的理解和应用,在和同学们的讨论中我见识到了大家许多非常巧妙的算法,这让我对之后的算法学习充满了兴趣和期待。

不过本单元的学习也并不算一帆风顺(之前听说只有第一单元和第二单元比较折磨),在每次作业都会新增很多新的指令,并且还会对之前已有的指令实现和结构产生未知的影响,所以这单元出现的bug数量明显上升,还有出现过的cpu超时以及内存过大等问题,确实令人头大。

不过好在本单元的三次作业都顺利完成了,无论强测分数如何,本单元都已过去,希望在之后的第四单元可以取得自己满意的成绩吧!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。