301
社区成员
发帖
与我相关
我的任务
分享目录
测试过程
黑箱测试 vs 白箱测试
单元测试 & function & 集成测试 & 压测 & 回归测试
数据构造
架构设计
性能 & 规格与实现分离
JUnit
学习体会
黑箱测试中,我们只关注代码整体的正确性。具体来说,测试数据并喂给程序,检查输出是否符合预期。 感觉这其实是我们平时最常用的debug方法了:输出不对,步步定位。
白箱测试中,关注内部结构和实现细节,以测试代码的逻辑路径、数据流和覆盖率等方面。白箱测试通常包括:
语句覆盖(Statement coverage):确保每个代码语句都至少执行一次。
判定覆盖(Decision coverage):确保每个条件语句的每个分支都至少执行一次。
条件覆盖(Condition coverage):确保每个条件语句中的每个子条件都至少取到一次真值和一次假值。
路径覆盖(Path coverage):确保测试用例覆盖代码中的每条可能路径
这两者都是黑箱,就是看整体的代码能不能完成功能、并在大数据的情况下不垮台。
单元测试是针对软件的最小可测试单元进行的测试,通常是对代码中的函数、方法或类进行独立的测试。(也就是我们写的JUnit)
集成测试是测试软件模块之间的接口和交互,确保它们能够正确地集成,并且组合起来能够达到预期的功能和性能。
在回归测试中,要保证迭代过程中加入的新代码没有导致既有功能产生错误,通常会重复之前进行过的测试,用相同的数据进行测试。
常用的当然是随机。
但是,很多情况下,人造数据更有杀伤力。比如这次作业,虽然hack限制了3000条指令,但是通过合理计算并分配,当某些方法的实现为O(n^2)时,仍然可以造出10^9的数据。
此次作业感觉不需要像前两个单元一样花心思设计架构。因为规格都给好了,架构也是完全给好了,甚至没有改的余地。(当然,为了优化做了并查集;不过这个也只是依附于整体架构上的一个小装饰品)
只要保证单次请求的复杂度不大于O(n) ,就不会有性能问题。(虽然这可能并不很容易,许多数据需要动态维护,还是挺麻烦的)
比如,tagValueSum这儿,写一个O(n^2)的极其容易,但是一些情况下会爆;而动态维护比较麻烦,但不会出现性能问题。
规格虽然提供了一个实现方法,但是其实可以看作一个黑箱:告诉你输入和输出的逻辑。
而具体逻辑就是黑箱里面的东西。怎么实现,看需求。比如,tagValueSum这儿,写一个O(n^2)的极其容易,但是一些情况下会爆;而动态维护比较麻烦。这时,就要算一算,到底会不会出现超时的情况,如果不会,那何必呢……(当然,在我们的作业中,是会的)
我认为主要有一下几点需要注意:
理解 JML 规格信息;
根据规格编写测试用例;
检验规格和实现一致性;
调试和修复;
持续迭代测试。
效果上其实主要取决于JUnit的测试代码的实现质量,如果JUnit测试没有考虑到,那也无法实现代码实现与规格的一致性。
这一单元集中于具体实现,而没有对架构的设计。这降低了很多难度。
当然,其实还有一个问题值得思考:当已经有一个设计好的架构时,应该如何进行具体实现,才可以让这个架构不至于凌乱呢?(在我们给这个骨架填上肉时,其实很多时候这副骨架原本比较完美的骨头已经被弄歪了……)
另外,JML一言难尽。实现一个方法的JML比实现这个方法本身要困难。更关键的,实现JML要求实现者对这个设计架构与方法本身有着极其极其透彻的理解,否则几乎必然是有考虑不周的。 而且,阅读JML也很麻烦:很多时候,一句文字可以传达的意味,用JML却要一大堆且难以让人理解。深感20世纪曾推行的“逻辑语言”的不可行之处——太准确太公式化,缺乏了表情达意的空间。也更感到“人之精英为语言”背后的奥义。
毕竟了解还是不多,不能做什么结论性的评价,只是一些讨论与思考。