301
社区成员
发帖
与我相关
我的任务
分享前两单元给我一种开创我代码新时代的豪情,这一单元给我的最大感受就是平静,平静地看规格,平静地迭代,平静的生活固然大家都在称赞,但是总感觉陷入了桎梏,不过也好,至少不用像之前一样每天都在战斗,爽!,可能这才是软件开发的常态?
黑箱测试:只需要关注输入输出,根据规格说明或用户需求构建测试样例,完全不需要理会内部结构。
例子:在自建评测机时随机生成测试样例,将结果与预期进行比对。
白箱测试:已经掌握了源代码,了解程序的内部逻辑和结构,根据这些源代码构建测试样例,尽可能覆盖程序的所有路径。
例子:在oo pre中要求的覆盖率测试,就是一种白箱测试,通过覆盖率确保每个分支都不出问题。
运用:经过这一单元的测试工作,我发现这两种测试都有各自的问题
(在第二次作业漏掉了1111这个条件导致强测错了一个点),再比如我认为personId不可能出问题,直接采用0-10000顺序生成,结果因为(int溢出和初值问题)导致有同学因此错了点。bestAccqaintance的初值设置为-1导致错误,覆盖率测试确实显示覆盖到了这一条,但是它就是疏漏,白箱测试很难发现,但黑箱测试会发现这个问题。juint,对queryTripulesum等易错方法进行测试,这就属于单元测试。timeout等限制,只考虑正确性,就能进行功能测试。TripleSum的性能是否会超出限制。而在构建评测机时也可以通过抬高并发量模拟高并发等极端条件,发现TLE等问题。junit要求TLE,在官方评测机大概率不出问题。本单元的junit编写还是挺考验测试数据编写水平的,当然这也演化出了两个方向:
oo pre时代就广为使用network中的person和tag以及message等,然后直接调用并测试指定方法。我主要走的是第二个方向,其原因主要在于方便对数据进行备份,以防被测试的代码有悄悄改变数据的功能。
我采用的是少量受控随机的策略:
deleteColdEmoji的主要依据是limit,然后发展出删和不删两个分支,也就是说我们甚至都没必要去sendMessage,因为初始所有heat为0,limit取0会不删除,取>0就会全部删除,这样已经测试了这两个分支,没有必要取增加send,这会极大增加复杂度。message就行了,注意前后一致性的检查即可一次通过。Junit部分基本按照JML组织架构,做出的明显改动主要是设计了ExcepionCounter静态类,该类拥有各自静态的异常计数器实例,大大简化了异常类的构建:
public MyAcquaintanceNotFoundException(int id) {
this.id = id;
counter = ExceptionCounter.getAcquaintanceNotFoundCounter();
//获取自己的计数器
counter.NewException(id);
//计数器++
}
@Override
public void print() {...
}
另一个明显改动是将并查集做成了单独的类,进行解耦。
最终图为

demo使用了Arraylist纯按JML写,从此留下了卡到爆的心理阴影,之后几乎都是用了hashmap、hashset。
第一次作业:主要是查询是否连通这一操作费时间。对此,采用了维护并查集的策略,在新增人、关系和删除关系时进行维护。采用了局部更新的策略,通过广度优先遍历找到断关系时需要分开的人,然后对这部分进行重建并查集,性能还不错。
第二次作业:主要费时间的是
O(n^2),这是O(m+n),并不会造成过度复杂。benchmark ,第三次作业:
经过
TLE的提心吊胆,彻底理解了这一点。
O(n^2)的方法,一定不要囿于规格,必须尝试高性能算法。本次共计出现一个bug(为什么每次搭评测机偷懒都会出bug{{{(>_<)}}},上一次是第一单元hw2没测合法性错了俩),因为在做时忘记了1111这个限制条件并且做评测机时认为不会有人错就没有测这个。
其它主要讲讲hack别人的情况吧:
前两次作业主要是抓别人TLE,直接用评测机生成一些比较阴险的数据(大量高复杂度操作),并且把本地评测机限时拉到3秒5(是的,有房友本地3秒9交上去被我爆了)。
另外,对于qtvs操作可以用数学方法找一些极限数据,比如用函数计算出3000条数据的话,将600-700人加到一个tag里然后反复查计算次数会达到极值,可以直接爆按着jml写的同学。
第三次作业主要是有房友爆除零异常,给大伙送福利了。
私下hack到了两位同学,都是因为没有注意到第三次作业可能产生同id的两个tag对象,因为message里的那个 tag引用不会被回收, 脱离person后会直接失去控制。
评测机的不足:没测id问题导致有房友逃过一劫,id这么简单,怎么会出问题呢。有同学在找最好邻居时使用优先队列并且采用id相减,这样如果相减超出int,就会发生可怕的事情。
在上面讲了Junit的数据构造策略,此处再来补充一些Junit的使用技巧
测试,不如说是攻防战。一定要全面检测是否有数据被暗改,可以用深克隆或者“影子”数据备份。Junit相比常规评测机的方便之处大概在于能够直接在内部构造指定数据进行单独测试,而且提供了覆盖率这一指标。在本单元中要测试的方法覆盖满了基本都能一次通过,不需要太考虑什么极端情况。Junit的编写依据主要是规格,数据生成要符合规格,并且,一致性,尤其是assignable、pure,一定要注意。Junit效果还是不错的,基本写了Junit的方法都不会在其它测试中出现什么问题,也验证了Juint在检验代码规格一致性上的效果还是不错的。感觉这一单元安逸中透露着焦虑。
benchmark解决。JML 与 JunitJML初衷是好的,直接用规范化的语言描述规格,避免了自然语言的不确定性。本单元第三次作业有很多同学因为message的tag和人持有的tag可能发生重复而写出bug,如果用自然语言描述的话,我确实不敢想象混乱程度。JML而是要求阅读也不错,工作量不是很大,挺休闲的。Juint测试过的方法基本没见bug,说明它确实有效。而且经历了三次Junit编写,对数据构造水平也有所提升。不过一点小问题就是要测的出错的代码确实略显离谱,assingale的测试确实是有点小难的。感觉自建评测机和Junit可以互补,对易错方法上个Junit保个险,其它用评测机测测,感觉会是个不错的选择。