首页 > 其他分享 >北大李戈团队提出:利用分片思想生成复杂函数的单测方法

北大李戈团队提出:利用分片思想生成复杂函数的单测方法

时间:2024-09-06 17:05:07浏览次数:6  
标签:李戈 -- 代码 value method 单测 分片 total 复杂度

单元测试的目标是:通过对代码的最小单位进行独立测试,提高代码的可靠性,降低引入新错误的风险,确保代码功能的正确性,并提供一种持续验证的机制,帮助开发者保持代码的高质量和可维护性。

环复杂度

对待测函数的复杂度,有个指标:环复杂度,也称为圈复杂度或圈复杂度度量(Cyclomatic Complexity,简称 CC),是衡量一个程序的逻辑复杂度的指标。

它表示程序的独立路径数量,或者说是代码中可能存在的独立执行路径的数量。

它的计算基于程序的控制流图(Control Flow Graph),通过分析代码中的分支、循环、条件语句等控制结构来确定,环复杂度的计算公式:

CC=E−N+2P

其中:

  • 是控制流图中的节点数。控制流图中的每一个节点对应于程序中的某些基本执行块(如一段没有分支的代码)。每个节点之间通过控制流(边)相连。比如一个简单的赋值语句或输出语句通常可以被认为是一个节点。

  • 是控制流图中的边数。边代表从一个节点到下一个节点的控制流。例如,一个 if-else 语句就会产生两条边,分别对应于条件成立时和条件不成立时的两个不同路径。

  • 是程序中独立的流程分支(例如子程序或函数)。对于大多数程序或函数来说,独立的流程分支通常是 1(即从唯一的入口开始执行)。但如果程序中有多个函数或子程序,独立流程分支的数量会增加。

假设我们有以下一段简单的代码:

def process_data(data):
    if data is None:
        return "No data"
    
    total = 0
    index = 0
    
    while index < len(data):
        value = data[index]
        if value > 100:
            total += value
        elif value > 50:
            total += value / 2
        else:
            total += 1
        
        if value % 2 == 0:
            total *= 2
        
        index += 1
    
    if total > 1000:
        return "High"
    elif total > 500:
        return "Medium"
    else:
        return "Low"

这段代码的控制流图如下:

graph TD; A[Start] --> B{"data is None?"}; B -->|Yes| C["return 'No data'"]; B -->|No| D["total = 0, index = 0"]; D --> E{"index < len(data)?"}; E -->|No| F{"total > 1000?"}; E -->|Yes| G["value = data[index]"]; G --> H{"value > 100?"}; H -->|Yes| I["total += value"]; H -->|No| J{"value > 50?"}; J -->|Yes| K["total += value / 2"]; J -->|No| L["total += 1"]; I --> M{"value % 2 == 0?"}; K --> M; L --> M; M -->|Yes| N["total *= 2"]; M -->|No| O; N --> O; O --> P["index += 1"]; P --> E; F -->|Yes| Q["return 'High'"]; F -->|No| R{"total > 500?"}; R -->|Yes| S["return 'Medium'"]; R -->|No| T["return 'Low'"];

上面代码的环复杂度为:

  • 节点数 (N): 20
  • 边数 (E): 23
  • 程序只有一个入口点,所以 P = 1。
CC=E(23)−N(20)+2P(1) = 5

通常来说,环复杂度越高,代码的可测试性、可维护性和错误率都会受到影响。一般的经验法则是,环复杂度为 1 表示线性代码,没有条件或循环,数值越高,意味着代码中有更多的分支或循环。

当环复杂度大于 10 时,意味着这个函数或代码片段中包含了相当多的条件分支、循环、或是多重嵌套的逻辑路径。这种情况下,生成高覆盖率的测试样例变得非常困难,因为要确保每一个独立路径都得到充分测试,可能需要非常多的测试样例。复杂度越高,路径的组合数量呈指数增长,因此大模型(例如大语言模型或生成模型)生成覆盖所有路径的测试集就变得非常具有挑战性。

利用分片思想生成复杂函数的单测

随着AI的进步,让AI产生单测会提效很多。
但是大模型自身无力为复杂待测函数(环复杂度大于10)生成高覆盖率的测试样例。针对该痛点,北京大学李戈教授团队提出一种方法切片的解决方案 HITS (High-coverage LLM-based Unit Test Generation via Method Slicing 基于方法切片的高覆盖率LLM单元测试生成):

将复杂的方法分解成多个切片(小块)。然后,逐个切片让LLM生成相应的测试用例。

通过这种方法,LLM的分析范围得到了简化,使其更容易覆盖每个切片中的更多代码行和分支。

相关论文:

https://arxiv.org/pdf/2408.11324

主线流程:

1、程序分片

程序分片指将一个程序依据语义划分为若干解决问题的阶段。程序是对一个问题解决方案的形式化表述。一个问题解决方案通常包含多个步骤,每个步骤对应着程序中的一片(slice)代码。

如下图所示,一个色块对应着一片代码,也对应着一个问题解决的步骤。

该论文给出的程序分片的Prompt如下:

Please help me break down the method under test into
multiple slices
Here are the basic details about the method under test
{{ focal method }}
{{ dependencies }}
Please decompose the method under test following the
instructions
1. Summarise the focal method
2. List necessary env settings to run the focal method
- Invoked paremeters and fields
- Invoked methods
3. Decompose the focal method into multiple problemsovling steps
3.1 describe the subtask of the slice
3.2 replicate the corresponding code statements
Please output following the fomart:
...

2、构造prompt指示大模型生成初始测试样例

接下来,构建思维链(Chain-of-thought)形式的prompt引导大模型生成测试样例,推理步骤如下:

  1. 列举该代码块中使用的所有变量和方法:
  2. 分析并列举该代码块需要处理的所有类型的条件。
  3. 对于代码块中的每个条件,设计测试用例以涵盖对应的所有代码行。注意要针对每个条件组,推断如何构造方法的输入参数和依赖的对象或类。

对应Prompt如下:

Please help me generate tests to fully cover a slice
Here are the basic details about the slice under test
{{ focal method }}
{{ dependencies }}
{{ invoked parameters, fields, methods}}
{{ slice }}
Please generate a whole unit test file covering all branches of
the slice following the giving steps
1. Enumerate all used variables and methods wiithin this block:
2. Analyze and enumerate all types of conditions that this block
is expected to handle. For instance:
... ( the example )
3. For each condition within the block, devise test cases to
encompass all corresponding lines. To achieve this:
For each condition group, infer how to construct the method's
input parameters and dependent objects and classes. For example:
... ( the example
#### Requirements and Attention for the Unit Test to Generate:...
#### Guidelines for Generating Unit Tests with Comprehensive
Coverage and Error-Free Execution: ...
### Examples
### Output Format:
...

3、通过后处理和self-debug使大模型生成的测试样例得以正确运行

大模型生成的测试样例往往难以直接使用,会出现各式各样的编译错误和来自于错误编写测试样例导致的运行时错误。

研究团队根据自身观察及已有论文的总结,设计了若干规则和常见错误的修复案例。

首先尝试依据规则修复。如果规则无法修复,则使用大模型self-debug的功能进行修复,在prompt中提供了常见错误的修复案例以供大模型参考。

Prompt中修复单元测试的步骤也是用的思维链方式的:

  1. 找出发生错误的语句
  2. 解释错误的原因
  3. 给出如何修复错误的解决方案
  4. 提供完整的修复单元测试,使用JUnit 5。

对应Prompt如下:

{{ focal method}}
{{ dependencies}}
Please help me fix the non-executable tests
{{ non-executable unit tests }}
{{ error information}}
# Procedures for Fixing the Unit Test:
Let's proceed step by step:
Please output following the fomart:
1. Pick out the statements that the errors occur
2. Explain the causes of the errors
3. Give solutions on how to fix the errors
3. Provide the complete fixed unit test, utilizing JUnit 5.
# Requirements and Considerations for the Unit Test Fix:
...
# Tips for Fixing the Unit Test: ...
# Examples of Healthy Unit Tests: ...
# Output Format
To facilitate generating the desired unit test, follow
these instructions: ...

效果

实验结果表明,与当前最先进的测试生成方法以及Evosuite等传统SBST(基于搜索的软件测试)方法相比,HITS方法在代码行和分支覆盖率方面有显著提升。

论文中给出了一个演示Case:

基线方法生成的测试样例未能完全覆盖Slice 2中的红色代码片段。

然而,HITS由于聚焦于Slice 2,对其所引用的外部变量进行了分析,捕捉到 “如果要覆盖红色代码片段,变量‘arguments’ 需要非空”的性质,根据该性质构建了测试样例,成功实现了对红色区域代码的覆盖。

标签:李戈,--,代码,value,method,单测,分片,total,复杂度
From: https://www.cnblogs.com/ghj1976/p/18400602/bei-da-li-ge-tuan-dui-ti-chu-li-yong-fen-pian-s

相关文章

  • C++ 嵌套类简单测试
    classDog{public:classAnimal{public:Animal(Dog*dog){m_Dog=dog;m_Age=1;m_Name=dog->m_Info;//可以访问宿主类对象}stringm_Name;intgetAge(){......
  • Go实现大文件分片上传
    index.html<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>uploadfile</title></head><bodyid="app"><h1style="text-align:center"&......
  • Mockito教程(单测mock)
    1Mockito介绍[3]1.1Mockito是什么?Mockito是mocking框架,它让你用简洁的API做测试。而且Mockito简单易学,它可读性强和验证语法简洁。1.2为什么需要Mock测试驱动的开发(TDD)要求我们先写单元测试,再写实现代码。在写单元测试的过程中,我们往往会遇到要测试的类有很多依赖,这些......
  • C++ 构造函数/析构函数中调用虚函数的情况简单测试
    #include<string>#include<iostream>usingnamespacestd;namespace{classAnimal{public:Animal(){cout<<"基类调用虚函数…"<<endl;cout<<GetInfo()<<e......
  • JAVA实现文件分片上传、大文件秒传
    该说不说,最近这块挻火的。今天早上有网友加我微信,也是咨询这块的技术问题,最近不知道啥情况,加我的网友还是挻多的。实际上我的微信很早就在网上公开了,但是还是有很多网友说找不到。昨天晚上论坛里面有位网友发私信给我,聊了一下这个问题,这个网友是一个自由职业者,他也是刚开始......
  • 一起单测引起的项目加载失败惨案
    一、前言最近在开发一个功能模块时,在功能自测阶段,通过使用单测测试功能的完整性,在测试单测联通性使用到静态方法测试时,发现单测报错,通过查阅解决方案发现需要对Javaassist包进行排包或者升版本处理。通过排包解决掉单测报错,在部署项目时发现频繁报bean注入失败问题,最终定位发现......
  • Mycat分片-水平拆分
    目录场景准备配置测试续接上篇:Mycat分片-垂直拆分-CSDN博客 场景在业务系统中,有一张表(日志表),业务系统每天都会产生大量的日志数据,单台服务器的数据存储及处理能力是有限的,可以对数据库表进行拆分。 准备准备三台服务器,具体的结构如下:(本次操作使用......
  • go的github.com/prometheus如何在单测中校验值是否正确
    假如我的指标定义如下:MetricGroupStatGauge=prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name:"test", Help:"test",},[]string{"name","age","sex"})...忽略对指标添加数据的代码那么如何取值进行校验呢?注意:GetMetricWithLabelValues(&......
  • 短视频上传怎么做|写个支持分片上传/断点续传/秒传功能的文件服务吧
    前言各位平时使用的短视频应用,微信&微博等图文社区,它们的图文动态&视频上传的能力,都是极其核心的业务。本质来说,这都是文件的上传,这篇文章带大家写一个文件上传服务,探究其核心原理,相信能为你带来一些帮助。感谢我的好友Trembling对本文的支持主要包含以下能力:文件上......
  • Redis集群之Redis分片集群
    前序:Redis集群搭建直接一步到位:支持海量数据以及高并发写分片集群顾名思义,将数据分开存储到Redis集群中,这样能够存储更多的数据,避免浪费资源,基础搭建如:三主三从(一拖一)、三主六从(一拖二),本次搭建采用一拖一,一拖二情况可根据文末图文介绍进行添加从节点即可cluster不能选择db,只......