301
社区成员
发帖
与我相关
我的任务
分享目录
分析作业中出现的性能问题及其修复情况,谈谈自己对规格与实现分离的理解
总结分析如何利用规格信息来更好的设计实现Junit测试,以及Junit测试检验代码实现与规格的一致性的效果
黑箱测试:通过输入数据和观察输出结果来验证软件的正确性。即搭评测机,捏数据,去验证。
白箱测试:需要了解软件的内部实现细节,包括代码逻辑、数据结构和算法等。即,看代码,找bug,肉眼观测是解决bug最简单粗暴的方法。
一般来说,我更喜欢白箱测试,毕竟是自己写的代码,自己理应对其很熟悉,白象测试的能力锻炼,一方面也是对自己代码基本功的良好锻炼,当自己白箱测试的能力非常强,大概率写出的bug也比较少,所以这是很重要的能力。
而黑箱测试是在自己难以解决问题的时候的必要选择(毕竟,不可能所有bug都能靠白箱测试解决),虽然会更耗时间,但是能便于发现白箱测试难以发现的bug。
而且黑箱测试相对有一个很大优点就是。对于我们单个人来说,白箱测试远比黑箱快,但是对于多人情况,黑箱可能更快。这时候白箱测试的话,由于我们去看别人代码细节,消耗的时间远比自己代码,花的多,所以黑箱测试就体现出优势,因为只要捏数据,他对于不同人写的同一个题目,是一样的。而且bug很大的特点就是,自己写的,自己看不出来,所以自己进行白箱测试,黑箱测试,可能都是关注相同的点,导致重复率很高,而适合于互相攻击(互测)的黑箱测试,在这时候就体现出优势了。非常便于将各个人的注意点都通过统一的接口——数据点,来连接。
所以我个人的策略是先进行一边白箱测试,再进行黑箱测试。
对软件中最小的可测试单元进行测试的过程。目的是验证单元的功能是否按照预期工作,对于我们的实验来说,相当于每次作业的junit,单独测试一个方法。
功能测试是对产品的各功能进行验证。对应到我们的作业中,就像是看写的各个指令能否正确执行。
集成测试是测试不同模块或组件之间的集成和交互,确保它们一起正常工作的过程。也就是对于代码整体进行测试,避免各个方法单独是对的,但是合起来就会产生一些问题。
构建极端数据来进行测试,以确定整个系统的稳定性。在我们作业中,就是各种方法的时间复杂度的把控,对于部分同学出现的id相减溢出等情况的考虑。
回归测试是在对软件进行修改或更新后重新运行之前的测试用例,以确保新修改未破坏系统的其他功能。这部分可能在代码量和难度越大的时候,越容易出现,比如多线程(x)。
针对各种时间复杂度大的方法捏造极端数据。(比如所有人都在同一个tag里)。因为随机数据,很难做到恰好达到时间复杂度的最大情况,但是手捏就很容易(x)
对图的特殊情况考虑,比如完全图,全孤立点,等。
架构大家都差不多,毕竟jml给出了规格了。图是用邻接图。新建一个MyMap来管理关于图的各个信息与算法实现。容器多用hash。
维护策略,对应复杂度超过O(n)的方法,一定要维护,也就是动态维护,每次修改时候更新。对于复杂度是O(n)的,选择性维护,因为不会因为这个超时,看自己的追求。(注意考虑嵌套情况下,比如一个方法是O(n),但是里面一定嵌套另一个O(n)的方法,达到O(n*n),那是一定要注意到的)
个人实现里,强测互测中没有出现过什么bug和性能问题。主要谈一下规格与实现分离和性能优化策略。
规格在一定程度上就像是一种代码的宽泛实现方式,用最基础的思路表达题目要求。而到了具体实现部分,与规格相符,但是不能一样。也就是,规格主要讲述正确性,实现要考虑复杂度等因素。两者的实现方式是不一样的。
而性能优化方面,主要采用Hash储存,便于查找,对复杂度高的方法都进行维护,对低的方法,选择性维护,比如,O(n)方法,如果维护的很容易就到O(1),并且不给其他方法带来太大负担,那就可以顺手维护一下。
算法方面,都是通用的,并查集,bfs,dfs,等,同时,脏位,缓存,等小技巧,结合。
性能方面,虽然我们说,维护的多,这个方法更快,但是,对于很多很多复杂度高的方法,我们去动态维护他的过程,都在给其他方法带来一定负担的前提下进行的,只不过是把一个O(n*n)方法优化,但是让其他一部分方法复杂度提高一点。当我们维护了非常多的情况,代码自然会变得臃肿。所以对于不必要优化的情况,可以不去优化,不然是降低代码可读性,简洁性。
构建各种情况的关系网络,在此基础上进行测试。
效果方面,能与白箱测试互补,用另一种思路来针对一个方法专门进行测试,提高正确性,但另一方面,想要真正做到完全保证,是很难的,也就是仅仅通过junit来保证代码完全正确,是极其困难的。比如验证pure方法,由于方法限制,深克隆方面,大家只能创建两个network,进行完全一样的操作,期望他会完全一样,但是即使代码正确,一样的步骤,也只能保证结果正确,不能保证中间情况完全一样。(极端的就是多线程,本次作业,在一些无关紧要的地方用个random,也会导致过程有一点不一样,但是结果一样的情况,比如放Person的Hashmap的key是random出来的话,每次遍历得到的person数组的顺序就会不一样了,很多人的测试就寄了,但是对于代码的功能实现,大不了就把他当做一个数组遍历,肯定能实现功能,这当然是极端情况,但对于小白,能确保不太会Hash的他不会那么干吗),而对于黑箱的我,无法确保他会做出什么操作,只能根据对于助教的说法,来猜测代码不会很离谱。但是都让我测试的程度了,怎么能确保写代码的人不离谱呢(x)
通过junit来进行正常情况下的功能判断,一致性检验还是可以做到的。在对于极高要求的企业,使用这种用时间换正确性的测试,还是可以的。
对于有了jml来书写代码,相对前两个单元,难度是有所减小。规格化的设计,如果可以让自己放心相信这个规格没问题,那确实是一件好事,给代码编写人员减压,更加关注代码优化(算法优化)。但是最大的问题就是,谁能确保这个规格一定没问题,编写jml这种如此接近编程语言的东西,其实难度还是不小的,甚至非常大,比直接写代码难度大。如果JML出错了,漏写了,那代码上优化的再好,最后也会是错的。所以这东西只能对于要求高,能力强的团队去使用,成员有能力去编写好JML,而且团队有精力去先写这种规格,再写代码。也是一种用时间换效果的方法。
但是这种在约定下框架去进行编写代码的方式也算是一种经验积累。(同时,不得不说,规格确定的情况下,由于架构明确,白箱测试别人代码的成本也是极大减少,对于卷王,完全可以通过在互测中看其他所有人的代码,去把所有人bug找出来,虽然我不是卷王,但看一个人的代码找bug时间,20分钟大概就可以完成一份,理论上这种卷王是可以做到用两三个小时时间,换取全组阴影(x))