BUAA OO JML 第三单元

阿得旺斯 学生 2024-05-16 11:44:46

测试过程

源代码地址

引言

本单元是JML单元,相比前两次思维量少得多,是以博客内容也相应调整。

黑箱测试与白箱测试

  • 黑箱测试:测试人员不需要了解软件内部的实现细节,只关注输入和输出之间的关系,通过给定的输入数据来验证软件的功能是否按照规格说明正常运行。优点是可以独立于编码人员进行,测试者可以从用户的角度出发,发现用户可能面临的问题。缺点是可能无法发现代码中的错误或逻辑缺陷,只能验证功能是否按照规格说明运行。
  • 白箱测试:测试人员需要了解软件的实现细节,通过检查代码逻辑、路径覆盖等来验证软件的正确性和健壮性。优点是可以发现代码中的错误、逻辑缺陷以及性能问题,对于提高代码质量有很大的帮助。缺点是测试相对复杂,需要测试人员了解程序逻辑。

单元、功能、集成、压力、回归测试

  1. 单元测试
  • 单元测试是针对软件中最小的可测试单元进行的测试,通常是对独立的函数、方法或模块进行测试。
  • 单元测试的目的是验证代码的正确性,确保每个单元按照预期工作。
  • 单元测试通常由开发人员在编写代码时进行,可以快速定位和修复问题,促进代码质量的提高。
  1. 功能测试
  • 功能测试是针对软件的功能需求进行的测试,验证软件是否按照规格说明和用户需求正常运行。
  • 功能测试关注软件的输入和输出,测试人员通过输入各种数据和情况来验证软件的功能是否正确。
  • 功能测试通常由专门的测试团队进行,在软件开发周期的后期进行。
  1. 集成测试
  • 集成测试是将已经经过单元测试的模块或组件进行组合,并验证它们在一起工作的测试。
  • 集成测试的目标是验证不同模块之间的接口和交互是否正确,以及整体系统是否符合预期。
  • 集成测试通常在功能测试之前进行,可以发现模块之间的集成问题和依赖关系的错误。
  1. 压力测试
  • 压力测试是为了评估软件在负载、并发和异常条件下的稳定性和性能而进行的测试。
  • 压力测试通过模拟大量用户、高并发访问等情况来测试系统的极限能力和承受能力。
  • 压力测试可以帮助发现系统资源不足、性能瓶颈或内存泄漏等问题,以优化系统的性能。
  1. 回归测试
  • 回归测试是在对软件进行更改或修复之后,重新执行旧的测试用例以确保新的更改没有引入新的错误。
  • 回归测试的目的是验证软件的修改不会破坏原有的功能和逻辑。
  • 回归测试可以避免因为修改引入新的问题,保持软件的稳定性和可靠性。

数据构造策略

  1. 随机测试:通过大量的随机数据来提高覆盖率,最大程度发现程序潜在的问题
  2. 边界测试:对于随机数据难以覆盖的极端情况与特殊情况,需要额外构造特殊的数据点进行测试
  3. 模拟数据:假如推测出程序潜在的问题或者随机测试发现问题,可以以问题为导向,由果溯因,构造数据量小、但是对等错误的数据
  4. 分类测试:可以根据程序不同的功能、使用情景,构造不同的样例分别测试

架构设计

基础架构

完全按照JML要求的架构

isCircle维护

建立连通树

// MyNetwork 伪代码
private HashMap<Integer,Integer> toTreeEnd; //构造一个连通的树
public void addPerson():
    toTreeEnd.put(person.getId(),null)

public void addRelation()if (!(getEndPoint(id1) == getEndPoint(id2))):
        toTreeEnd.put(getEndPoint(id1),getEndPoint(id2))

public int getEndPoint(int id)while (toTreeEnd.get(end) != null) :
         end = toTreeEnd.get(end)

BlockSum维护

// MyNetwork 伪代码
public void addPerson():
    sumBlock++

public void addRelation()if (!(getEndPoint(id1) == getEndPoint(id2))):
        sumBlock--

//当需要删除关系时,进行深度优先搜索

TripleSum维护

// MyNetwork 伪代码
public void addRelation():
   sumTriple += findCommon(id1,id2)

public void removeRelation():
   sumTriple -= findCommon(id1,id2)

public int findCommon(int id1,int id2):
    return sum: person that link id1 && link id2

最短路径问题

广度优先搜索

BestAcquaintance与CoupleSum

//MyPerson
private long bestID = Long.MAX_VALUE;
private long maxVa = Long.MIN_VALUE;
 public void buildLink(Person person, int value) {
        ……
        if (value > maxVa) {
            maxVa = value;
            bestID = person.getId();
        } else if (value == maxVa && person.getId() < bestID) {
            bestID = person.getId();
        }
    }

    public void removeLink(Person person) {
        ……
        if (person.getId() == bestID) {
            maxVa = Long.MIN_VALUE;
            bestID = Long.MAX_VALUE;
            if (acquaintance.size() != 0) {
                for (Integer key:acquaintance.keySet()) {
                    Person p = acquaintance.get(key);
                    if (maxVa < value.get(p)) {
                        maxVa = value.get(p);
                        bestID = key;
                    } else if (maxVa == value.get(p) && acquaintance.get(key).getId() < bestID) {
                        bestID = acquaintance.get(key).getId();
                    }
                }
            }
        }
    }

    public void addPerValue(Person person,int va) {
          //参考上面两个,注意va可能是负数
    }
//MyNetwork
    public int queryCoupleSum() {
        int ret = 0;
        if (persons.size() == 0) {
            return 0; }
        for (Integer ieKey:persons.keySet()) {
            MyPerson ieP = (MyPerson) persons.get(ieKey);
            if (ieP.getAcquaintance().size() != 0) {
                int j = ieP.findBestID();
                MyPerson jeP = ((MyPerson) persons.get(j));
                if (jeP.getAcquaintance().size() != 0) {
                    if (jeP.findBestID() == ieKey) { ret++; } } } }
        ret /= 2;
        return ret; }

qtvs指令处理

本人在这一个点ctle后,使用了两个方法:
一个是设置脏位,来表示qtvs的上一次结果有没有可能被修改,如果没有变化,则直接返回上一次的结果.
否则进行遍历。而遍历方式也要修改:
jml要求

    //Tag 
    /*@ ensures \result == (\sum int i; 0 <= i && i < persons.length; 
      @          (\sum int j; 0 <= j && j < persons.length && 
      @           persons[i].isLinked(persons[j]); persons[i].queryValue(persons[j])));
      @*/
    public /*@ pure @*/ int getValueSum();

原来的遍历,好像和JML一模一样,emmmmmmm,不是JML让这么写的吗(气)

for (Integer ieKey:persons.keySet()):
    for (Integer jeKey:persons.keySet()):
        if (persons.get(jeKey).isLinked(persons.get(jekey))):

现在的遍历

for (Integer ieKey:persons.keySet()) :
     for (Integer jeKey:((MyPerson) persons.get(ieKey)).getAcquaintance().keySet()) :
         if (persons.get(jeKey) != null) :

性能与规格实现

想要保证性能,就不能照搬JML,而是要在满足JML规格的情况下进行性能的优化。也就是规格与实现结果相同,过程不同。

Junit测试方法

本单元中测强制要求进行单元测试,这也让我第一次详细了解了这一测试方法,感觉收益颇多。
前两次作业,本人都是构建一个复杂的图,然后调用需要测试的方法,然后对方法结果和其它变量是否被修改进行测试。

//public class MyNetworkTest 
    public MyNetwork myNetwork = null;//课程组的实现,调用测试方法,对照组
    private OneNetwork oneNetwork = null;//本人的实现,调用测试方法,检测方法结果正确性
    private MyNetwork yingZi = null;//课程组的实现,不调用测试方法,检测方法是否对不允许修改的变量进行修改

    public void AssErt() throws PersonIdNotFoundException, RelationNotFoundException {
        Person [] olds = yingZi.getPersons();
        assertEquals(myNetwork.queryCoupleSum(),oneNetwork.queryCoupleSum());
        Person [] news = myNetwork.getPersons();
        assertEquals(olds.length,news.length);
        for (int i = 0;i < olds.length;i++) {
            boolean f = ((MyPerson) olds[i]).strictEquals(news[i]);
            assertEquals(f,true);
        }
    }

学习体会

一开始在思考第三单元是否有存在的必要,更何况五一期间还有作业,等写完了就没有再思考这个问题了。
这一单元相比前两个思维量比较小,大多数代码是翻译JML,但是翻译的时候也出现了许多问题,一个是没有及时更新JML,导致我有一个bug找了很久都没找到错误,后来才知道有个JML要修改,其次是翻译不是照搬,需要有自己的规格实现。

...全文
41 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧