用AI对一段代码进行单元测试

1 题目

测试代码如下:

#include <stdio.h>

#define MIN_RUN 32

// 插入排序算法
void insertionSort(int arr[], int left, int right) {
    for (int i = left + 1; i <= right; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= left && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

// 归并函数
void merge(int arr[], int left, int mid, int right) {
    int len1 = mid - left + 1;
    int len2 = right - mid;
    int L[len1], R[len2];

	int i = 0, j = 0, k;
    for (i = 0; i < len1; i++)
        L[i] = arr[left + i];
    for (j = 0; j < len2; j++)
        R[j] = arr[mid + 1 + j];

    i = 0;
	j = 0;
	k = left;

    while (i < len1 && j < len2) {
        if (L[i] <= R[j])
            arr[k++] = L[i++];
        else
            arr[k++] = R[j++];
    }
	
    while (i < len1)
        arr[k++] = L[i++];
    while (j < len2)
        arr[k++] = R[j++];
	while(i>j && len1<len2)
		arr[i++] = arr[j++];
}

// Timsort 算法
void timSort(int arr[], int n) {
    for (int i = 0; i < n; i += MIN_RUN)
        insertionSort(arr, i, (i + MIN_RUN - 1) < n ? (i + MIN_RUN - 1) : (n - 1));

    for (int size = MIN_RUN; size < n; size *= 2) {
        for (int left = 0; left < n; left += 2 * size) {
            int mid = left + size - 1;
            int right = (left + 2 * size - 1) < (n - 1) ? (left + 2 * size - 1) : (n - 1);
            merge(arr, left, mid, right);
        }
    }
}

利用AI工具对以上代码编写单元测试用例

2 说明

ai工具:文心大模型4.0 Turbo。
测试环境:在Linux环境下,用gcc、gcov和lcov。
测试方式:将以上代码放到sort.c文件中,用ai工具生成测试用例,放到test.c文件中,然后在Linux环境下编译运行,测试是否所有测试用例可以通过,用lcov生成html格式的测试报告。

3 操作过程

3.1 用AI优化需求

首先编写一个让ai生成单元测试代码的需求,然后发给ai让其优化一下。
输入

对这段代码编写单元测试案例,实现100%语句覆盖、判定覆盖、条件覆盖(CC)、条件/判定覆盖(C/DC)、条件组合覆盖(MCC)、修正判定条件覆盖(MCDC)。 
要求: 
1、验证结果是否正确,不用预期结果与输出结果对比的方式,用遍历输出结果,判断是否前一个数据比后一个小 
2、打印出哪些测试用例通过了,哪些测试用例没通过,生成一个结果测试报告。
3、除了生成一些常规的测试数据,再生成一些超长的,里面是随机数的输入数组,数组长度一个是500,一个是1000 
4、生成一些长度不同的输入数组,长度从100连续到299,共200组,里面都是随机数 
5、每个测试用例封装成一个函数。
6、对于第3条和第4条要求的测试用例,打印测试结果时要标明测试数组长度。
7、用malloc申请内存存放输入数据时,测试完要及时释放。
9、常规测试数据里类型多一些,都是一些容易测试出代码问题的数据。
10、测试代码和被测试代码不在同一个文件中,被测试代码已经写好不需要ai再写一遍,只需要生成测试代码

以上是一个我要输入给文心一言的需求,现在对这个需求进行优化,让文心一言输出效果更好

输出

为了优化您的需求描述,使其更清晰、具体,并引导生成更高质量的测试代码,以下是对原始需求的优化版本:
________________________________________
测试代码生成需求优化版
目标:
为给定的排序函数编写单元测试,确保实现100%的语句覆盖、判定覆盖、条件覆盖(CC)、条件/判定覆盖(C/DC)、条件组合覆盖(MCC)、修正判定条件覆盖(MCDC)。
具体要求:
1.	验证方法:
	不通过预期结果与输出结果对比,而是通过遍历输出结果,判断数组是否已排序(即前一个数据是否比后一个小)。
2.	测试报告:
	打印出哪些测试用例通过了,哪些没通过。
	生成一个详细的测试报告,包括测试用例名称和测试结果。
3.	测试用例生成:
	常规测试数据:
	包含多种类型的数据,如空数组、单元素数组、已排序数组、逆序数组、含重复元素的数组、特殊值数组(如最大/最小值、负数、零等)。
	这些数据应易于触发代码中的潜在问题。
	超长随机数组:
	生成两个长度分别为500和1000的随机数组。
	打印测试结果时标明测试数组长度。
	连续长度随机数组:
	生成长度从100到299的200组随机数组。
	每组数组长度连续递增,打印测试结果时标明测试数组长度。
4.	测试用例封装:
	每个测试用例应封装成一个独立的函数,便于管理和复用。
5.	内存管理:
	使用malloc申请内存存放输入数据时,测试完成后要及时释放内存,避免内存泄漏。
6.	测试代码与被测试代码分离:
	假设被测试代码已经写好,只需编写测试代码。
	测试代码应位于与被测试代码不同的文件中。
优化后的详细步骤:
1.	编写测试框架:
	创建一个测试框架,能够执行测试用例并记录结果。
	提供函数用于打印测试报告。
2.	实现测试用例:
	常规测试用例:
	空数组测试。
	单元素数组测试。
	已排序数组测试。
	逆序数组测试。
	含重复元素的数组测试。
	特殊值数组测试(如全为正数、全为负数、混合正负数、含零等)。
	超长随机数组测试用例:
	生成并测试长度为500和1000的随机数组。
	连续长度随机数组测试用例:
	生成并测试长度从100到299的200组随机数组。
3.	验证排序结果:
	在每个测试用例中,遍历排序后的数组,检查是否满足排序条件(前一个元素小于或等于后一个元素)。
4.	内存管理:
	确保在每个测试用例中,使用malloc分配的内存都在测试完成后被正确释放。
5.	生成测试报告:
	在所有测试用例执行完毕后,生成并打印详细的测试报告,包括每个测试用例的名称、测试结果(通过/失败)以及任何额外的信息(如数组长度)。
期望输出:
•	一个清晰的测试报告,显示所有测试用例的执行结果。
•	对于失败的测试用例,应提供足够的信息以便定位问题。
________________________________________
通过优化后的需求描述,您可以更清晰地指导文心一言生成高质量的测试代码,确保覆盖所有必要的测试场景,并生成详细的测试报告。

3.2 用AI生成单元测试代码

将被测试代码和需求发给AI,让其生成相应的单元测试代码。
输入

#include <stdio.h>

#define MIN_RUN 32

// 插入排序算法
void insertionSort(int arr[], int left, int right) {
    for (int i = left + 1; i <= right; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= left && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

// 归并函数
void merge(int arr[], int left, int mid, int right) {
    int len1 = mid - left + 1;
    int len2 = right - mid;
    int L[len1], R[len2];

	int i = 0, j = 0, k;
    for (i = 0; i < len1; i++)
        L[i] = arr[left + i];
    for (j = 0; j < len2; j++)
        R[j] = arr[mid + 1 + j];

    i = 0;
	j = 0;
	k = left;

    while (i < len1 && j < len2) {
        if (L[i] <= R[j])
            arr[k++] = L[i++];
        else
            arr[k++] = R[j++];
    }
	
    while (i < len1)
        arr[k++] = L[i++];
    while (j < len2)
        arr[k++] = R[j++];
	while(i>j && len1<len2)
		arr[i++] = arr[j++];
}

// Timsort 算法
void timSort(int arr[], int n) {
    for (int i = 0; i < n; i += MIN_RUN)
        insertionSort(arr, i, (i + MIN_RUN - 1) < n ? (i + MIN_RUN - 1) : (n - 1));

    for (int size = MIN_RUN; size < n; size *= 2) {
        for (int left = 0; left < n; left += 2 * size) {
            int mid = left + size - 1;
            int right = (left + 2 * size - 1) < (n - 1) ? (left + 2 * size - 1) : (n - 1);
            merge(arr, left, mid, right);
        }
    }
}

测试代码生成需求优化版

目标:
为给定的排序函数编写单元测试,确保实现100%的语句覆盖、判定覆盖、条件覆盖(CC)、条件/判定覆盖(C/DC)、条件组合覆盖(MCC)、修正判定条件覆盖(MCDC)。

具体要求:

验证方法:
不通过预期结果与输出结果对比,而是通过遍历输出结果,判断数组是否已排序(即前一个数据是否比后一个小)。
测试报告:
打印出哪些测试用例通过了,哪些没通过。
生成一个详细的测试报告,包括测试用例名称和测试结果。
测试用例生成:
常规测试数据:
包含多种类型的数据,如空数组、单元素数组、已排序数组、逆序数组、含重复元素的数组、特殊值数组(如最大/最小值、负数、零等)。
这些数据应易于触发代码中的潜在问题。
超长随机数组:
生成两个长度分别为500和1000的随机数组。
打印测试结果时标明测试数组长度。
连续长度随机数组:
生成长度从100到299的200组随机数组。
每组数组长度连续递增,打印测试结果时标明测试数组长度。
测试用例封装:
每个测试用例应封装成一个独立的函数,便于管理和复用。
内存管理:
使用malloc申请内存存放输入数据时,测试完成后要及时释放内存,避免内存泄漏。
测试代码与被测试代码分离:
假设被测试代码已经写好,只需编写测试代码。
测试代码应位于与被测试代码不同的文件中。
优化后的详细步骤:

编写测试框架:
创建一个测试框架,能够执行测试用例并记录结果。
提供函数用于打印测试报告。
实现测试用例:
常规测试用例:
空数组测试。
单元素数组测试。
已排序数组测试。
逆序数组测试。
含重复元素的数组测试。
特殊值数组测试(如全为正数、全为负数、混合正负数、含零等)。
超长随机数组测试用例:
生成并测试长度为500和1000的随机数组。
连续长度随机数组测试用例:
生成并测试长度从100到299的200组随机数组。
验证排序结果:
在每个测试用例中,遍历排序后的数组,检查是否满足排序条件(前一个元素小于或等于后一个元素)。
内存管理:
确保在每个测试用例中,使用malloc分配的内存都在测试完成后被正确释放。
生成测试报告:
在所有测试用例执行完毕后,生成并打印详细的测试报告,包括每个测试用例的名称、测试结果(通过/失败)以及任何额外的信息(如数组长度)。
期望输出:

一个清晰的测试报告,显示所有测试用例的执行结果。
对于失败的测试用例,应提供足够的信息以便定位问题。

输出

为了实现你要求的测试框架和测试用例,我们将创建一个新的C文件来包含测试代码,并确保与被测试代码(即timSort函数)分离。以下是详细的实现步骤:
1. 测试框架
首先,我们创建一个测试框架,能够执行测试用例并记录结果。
c复制代码
	#include <stdio.h>
	#include <stdlib.h>
	#include <stdbool.h>
	#include <time.h>
	
	// 假设 timSort 函数声明在这里(通常通过包含头文件)
	extern void timSort(int arr[], int n);
	
	// 检查数组是否已排序
	bool isSorted(int arr[], int n) {
	for (int i = 0; i < n - 1; i++) {
	if (arr[i] > arr[i + 1]) {
	return false;
	}
	}
	return true;
	}
	
	// 打印测试报告
	void printTestReport(const char* testName, bool result, int length = -1) {
	if (length == -1) {
	printf("Test Case: %s - %s\n", testName, result ? "Passed" : "Failed");
	} else {
	printf("Test Case: %s (Length: %d) - %s\n", testName, length, result ? "Passed" : "Failed");
	}
	}
	
	// 测试用例模板
	void runTestCase(const char* testName, int* arr, int n) {
	timSort(arr, n);
	bool result = isSorted(arr, n);
	printTestReport(testName, result, n);
	// 释放内存(如果适用)
	// 注意:在这个框架中,假设传入数组不是动态分配的,因此不需要释放
	}
2. 实现测试用例
接下来,我们实现具体的测试用例。
c复制代码
	void testEmptyArray() {
	int arr[0];
	runTestCase("Empty Array", arr, 0);
	}
	
	void testSingleElementArray() {
	int arr[1] = {1};
	runTestCase("Single Element Array", arr, 1);
	}
	
	void testSortedArray() {
	int arr[] = {1, 2, 3, 4, 5};
	runTestCase("Sorted Array", arr, 5);
	}
	
	void testReverseSortedArray() {
	int arr[] = {5, 4, 3, 2, 1};
	runTestCase("Reverse Sorted Array", arr, 5);
	}
	
	void testArrayWithDuplicates() {
	int arr[] = {3, 3, 3, 2, 2, 1, 1};
	runTestCase("Array With Duplicates", arr, 7);
	}
	
	void testSpecialValuesArray() {
	int arr[] = {INT_MAX, INT_MIN, 0, -1, 1, 99999, -99999};
	runTestCase("Special Values Array", arr, 7);
	}
	
	void testLongRandomArray(int length) {
	int* arr = (int*)malloc(length * sizeof(int));
	if (arr == NULL) {
	printf("Memory allocation failed for length %d\n", length);
	return;
	}
	
	// 填充随机数
	srand(time(NULL));
	for (int i = 0; i < length; i++) {
	arr[i] = rand() % 100000 - 50000; // 随机数范围 [-50000, 49999]
	}
	
	runTestCase("Long Random Array", arr, length);
	
	// 释放内存
	free(arr);
	}
	
	void testSequentialLengthRandomArrays() {
	for (int length = 100; length < 300; length++) {
	testLongRandomArray(length);
	}
	}
	
	int main() {
	// 运行常规测试用例
	testEmptyArray();
	testSingleElementArray();
	testSortedArray();
	testReverseSortedArray();
	testArrayWithDuplicates();
	testSpecialValuesArray();
	
	// 运行超长随机数组测试用例
	testLongRandomArray(500);
	testLongRandomArray(1000);
	
	// 运行连续长度随机数组测试用例
	testSequentialLengthRandomArrays();
	
	return 0;
	}
3. 解释与注意事项
•	测试框架:runTestCase函数用于执行排序并检查结果,同时打印测试报告。
•	测试用例:每个测试用例都是一个独立的函数,便于管理和复用。
•	内存管理:在testLongRandomArray中,使用malloc分配内存,并在测试完成后使用free释放内存。
•	随机数组生成:使用rand函数生成随机数,并用srand(time(NULL))确保每次运行产生不同的随机数序列。
•	测试报告:printTestReport函数用于打印每个测试用例的结果,包括名称、是否通过以及数组长度(如果适用)。
这个测试框架和测试用例覆盖了多种可能的输入情况,确保了较高的代码覆盖率,并能够帮助定位潜在的问题。

3.3 搭建测试环境

将ai生成的代码放到test.c中,并做一些优化。内容如下:

#define MIN_RUN 32

#define INT_MAX 2147483647
#define INT_MIN -2147483648

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
 
// 假设 timSort 函数声明在这里(通常通过包含头文件)
extern void timSort(int arr[], int n);
 
// 检查数组是否已排序
bool isSorted(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        if (arr[i] > arr[i + 1]) {
            return false;
        }
    }
    return true;
}
 
// 打印测试报告
void printTestReport(const char* testName, bool result, int length) {
    if (length == -1) {
        printf("Test Case: %s - %s\n", testName, result ? "Passed" : "Failed");
    } else {
        printf("Test Case: %s (Length: %d) - %s\n", testName, length, result ? "Passed" : "Failed");
    }
}
 
// 测试用例模板
void runTestCase(const char* testName, int* arr, int n) {
    timSort(arr, n);
    bool result = isSorted(arr, n);
    printTestReport(testName, result, n);
    // 释放内存(如果适用)
    // 注意:在这个框架中,假设传入数组不是动态分配的,因此不需要释放
}

void testEmptyArray() {
    int arr[0];
    runTestCase("Empty Array", arr, 0);
}
 
void testSingleElementArray() {
    int arr[1] = {1};
    runTestCase("Single Element Array", arr, 1);
}
 
void testSortedArray() {
    int arr[] = {1, 2, 3, 4, 5};
    runTestCase("Sorted Array", arr, 5);
}
 
void testReverseSortedArray() {
    int arr[] = {5, 4, 3, 2, 1};
    runTestCase("Reverse Sorted Array", arr, 5);
}
 
void testArrayWithDuplicates() {
    int arr[] = {3, 3, 3, 2, 2, 1, 1};
    runTestCase("Array With Duplicates", arr, 7);
}
 
void testSpecialValuesArray() {
    int arr[] = {INT_MAX, INT_MIN, 0, -1, 1, 99999, -99999};
    runTestCase("Special Values Array", arr, 7);
}
 
void testLongRandomArray(int length) {
    int* arr = (int*)malloc(length * sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed for length %d\n", length);
        return;
    }
    
    // 填充随机数
    srand(time(NULL));
    for (int i = 0; i < length; i++) {
        arr[i] = rand() % 100000 - 50000; // 随机数范围 [-50000, 49999]
    }
    
    runTestCase("Long Random Array", arr, length);
    
    // 释放内存
    free(arr);
}
 
void testSequentialLengthRandomArrays() {
    for (int length = 100; length < 300; length++) {
        testLongRandomArray(length);
    }
}
 
int main() {
    // 运行常规测试用例
    testEmptyArray();
    testSingleElementArray();
    testSortedArray();
    testReverseSortedArray();
    testArrayWithDuplicates();
    testSpecialValuesArray();
    
    // 运行超长随机数组测试用例
    testLongRandomArray(500);
    testLongRandomArray(1000);
    
    // 运行连续长度随机数组测试用例
    testSequentialLengthRandomArrays();
    
    return 0;
}

将被测试代码放到sort.c中,编写一个脚本用于编译生成可执行文件,然后运行并生成测试覆盖率报告。测试目录内容如下:
在这里插入图片描述
其中test.sh的内容如下:
在这里插入图片描述
sort.c是被测试代码。

3.4 第1次测试

运行test.sh脚本,生成的测试报告如下:
在这里插入图片描述
从测试报告可以看出,在处理长度为129的数组时,程序运行发生的错误,在sort.c文件中加一些打印信息用于调试,添加过程如下:
在这里插入图片描述
然后只测试长度129的数组,运行结果如下:
在这里插入图片描述
可以看到出错原因是因为运行到这里时,mid比right的值大,而merge函数内容如下:
在这里插入图片描述
当mid大于right时,len2的计算结果为负数,这使得声明数组R时出现错误。所以当mid大于right时,表示不能分成两段进行合并,因此就不需要合并。根据以上分析优化代码如下:
在这里插入图片描述
再一次测试,测试用例全部通过。

3.5 第2次测试

在分析第一个问题时发现,merge函数中声明L数组和R数组的方式有些问题,代码如下:
在这里插入图片描述
这种方式声明的L数组和R数组存放在栈空间,栈空间相对较小,当输入的排序数组过大时,可能会出现内存溢出。为了验证是否会出现内存溢出,增加一个100M的输入数组用来测试。修改test.c内容如下:
在这里插入图片描述
用修改完的test.c文件进一步测试,发现果然在处理100M大数组时出现程序运行错误,如下:
在这里插入图片描述
根据以上分析,决定将L和R用malloc的方式申请内存,使其数据存放到内存较大的堆空间,修改代码如下:
在这里插入图片描述
然后再一次测试发现测试用例全部通过,测试结果如下:

在这里插入图片描述
在这里插入图片描述
这时查看代码运行覆盖率报告,结果如下:

在这里插入图片描述
可以看到其中有一段代码没有被运行到。

3.5 第3次测试

以上所写操作方式并不是第一次就是这么做的,经历过多次的迭代,初期让ai生成测试代码时,并没有附加多条要求,这时ai生成的测试代码总是有几条无法覆盖。将覆盖率报告反馈给ai让其优化测试代码时,ai指出来一段错误代码,如下:
在这里插入图片描述
原始代码如下:

在这里插入图片描述
前经过前面的代码处理后,这时应该i=len1、j=len2,所以不会出现(i>j && len1<len2)的情况。而且前面步骤已经将数据处理完毕,所以45行、46行是错误的代码行。
将错误代码删除,修改如下:

在这里插入图片描述
用修改之后的代码再测试,此时测试用例全部通过,并且代码运行覆盖率为100%,结果如下:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值