301
社区成员
发帖
与我相关
我的任务
分享黑箱测试:是从一种从软件外部对软件实施的测试,也称功能测试或基于规格说明的测试。其基本观点是:任何程序都可以看作是从输入定义域到输出值域的映射,这种观点将被测程序看作一个打不开的黑盒,黑盒里面的内容(实现)是完全不知道的,只知道软件要做什么。因无法看到盒子中的内容,所以不知道软件是如何实现的,也不关心黑盒里面的结构,只关心软件的输入数据和输出结果。
黑箱测试主要关注以下几个方面:
白箱测试:也称结构测试或逻辑驱动测试,它是知道产品内部工作过程,可通过测试来检测产品内部动作是否按照规格说明书的规定正常进行,按照程序内部的结构测试程序,检验程序中的每条通路是否都有能按预定要求正确工作,而不顾它的功能。
白箱测试主要关注以下几个方面:
单元测试通常是开发过程中的第一步,主要用于验证每个单元的功能是否正确。开发人员在编写代码的同时或之后进行单元测试,即对软件中的最小可测试单元进行检查和验证(在面向对象编程中通常是类或方法),以确保代码的各个部分(或“单元”)按预期工作。
当所有的单元测试通过后,接下来就是集成测试。在这个阶段,各个单元会被组合在一起进行测试,作为一个整体进行测试,以帮助发现单元之间的接口问题。
功能测试通常在集成测试之后进行,是一种黑箱测试方法,它关注的是软件的功能需求。功能测试确保软件的所有功能都按照预期工作。
压力测试通常在功能测试之后进行,这是一种性能测试,用于确定系统在高负载或压力条件下的行为。它可以帮助识别系统的弱点和限制,用于测试系统在极限条件下的性能和稳定性。
最后,每当软件的代码发生变化(例如,修复了一个错误,或者添加了一个新的功能),都需要进行回归测试。回归测试确保这些更改没有引入新的错误,或者使旧的错误再次出现。
在给定JML的基础上架构并没有太多的自由发挥的空间,但在此基础上我的独特架构包括:
DisjointSet并查集,封装成单独一个类,用于第一次作业的qci指令。
数据结构
private final HashMap<Integer, MyPerson> persons;
private final HashMap<Integer, Integer> father; // 节点->父节点
private final HashMap<Integer, Integer> rank; // 节点->秩
private int blockSum;
private final HashSet<Integer> color; // 用于删除边时dfs遍历染色
基本方法
private void merge(int id1, int id2) {
int fa1 = findFather(id1);
int fa2 = findFather(id2);
if (fa1 == fa2 && id1 != id2) {
return; //如果父节点相同,根本就没有必要合并
}
int rank1 = rank.get(id1);
int rank2 = rank.get(id2);
if (rank1 < rank2) {
father.put(fa1, fa2); //把秩较小的节点合并到秩较大的节点上
} else {
if (rank1 == rank2) {
rank.put(fa1, rank1 + 1); //秩相同的话,随便合并一下
}
father.put(fa2, fa1);
}
blockSum--; //分支数减一
if (blockSum < 0) {
System.err.println("error: blockSum is negative");
}
}
private int findFather(int id) {
int rep = id; //一开始代表元素为本身
while (rep != father.get(rep)) { //循环获得id所在分支的代表元素
rep = father.get(rep); //令rep迭代至顶层
}
int now = id;
while (now != rep) { //压缩路径,并将新对应关系放入fa中
int fa = father.get(now);
father.put(now, rep);
now = fa;
}
return rep;
}
为并查集最基本的两个方法。
注意:删除边的时候需要进行dfs遍历:
public void addRelation(int id1, int id2) {
if (father.containsKey(id1) && father.containsKey(id2)) {
merge(id1, id2);
}
}
public void subRelation(int id1, int id2) {
father.put(id2, id2);
color.clear();
dfs(id1, id1);
if (father.get(id2) != id1) {
blockSum++;
color.clear();
dfs(id2, id2);
}
}
private void dfs(int id0, int id) {
color.add(id);
for (Integer o : persons.get(id).getAcquaintances().keySet()) {
if (!color.contains(o)) {
dfs(id0, o);
}
}
father.put(id, id0);
}
最后查找两个id是否处于同一block的时候还需要进行查找父节点:
public boolean isSameBlock(int id1, int id2) {
if (id1 == id2) {
return true;
}
return Objects.equals(findFather(id1), findFather(id2));
}
Triple单独维护triple的类,在关系改变的时候维护它。
public void addRelation(MyPerson person1, MyPerson person2) {
HashSet<Integer> sameAcquaintance = new HashSet<>(person1.getAcquaintances().keySet());
sameAcquaintance.retainAll(person2.getAcquaintances().keySet());
tripleSum += sameAcquaintance.size();
}
subRelation同理。
ExceptionCounter用于计算异常数量的计数器类,实例化的时候要加上static关键词。
主要采用HashMap类型,即由id映射到单个对象,例如位于MyNetWork中的persons、messages等。
三次作业中很多地方都用到了动态维护策略,例如计算valueSum的时候,在一个Tag中加入一个人就要改变valueSum的值,同理删掉人的时候也要减去相应的值。而在删掉关系的时候,需要遍历二者的共同好友的所有Tag,在这些Tag中维护valueSum。
第一次作业中用到的算法为并查集
第二次作业中用到的算法为bfs
第三次作业中没用到什么特别的算法
此外,三次作业中都需要进行动态维护一些值
规格和实现分离是一种设计原则,它强调将问题的描述(规格)与解决问题的方法(实现)进行分离。这种原则有以下几个优点:
在面向对象设计中,规格通常体现在接口或抽象类或JML中,而实现则体现在实现这些接口或抽象类的具体类中。这样,只要新的实现满足原有的规格,就可以无缝地替换旧的实现,从而提高了代码的可维护性和可扩展性。这也是许多设计模式(如策略模式、工厂模式等)的基础思想。
在本单元中,JML以及抽象的接口定义了代码的规范,而具体的实现则可以由程序员进行自主实现,在这其中就会涉及到不同的算法导致不同的性能的问题。
JML是一种较为规范的描述语言,但是同时也存在着传递信息效率不高的弊端。但是在一些对精度要求高的项目中JML肯定能发挥重要的作用。总体来讲本单元较为轻松。