西电软工程序测试上机
找个做过的大作业做黑白盒测试。黑盒测试就是展示功能,白盒麻烦一点,这篇文章主要讲白盒测试怎么办。
我是用了c++写的贪吃蛇小游戏代码。友情提醒:找个class写的那种,比较方便。
工具
Microsoft Visual Studio (+chatgpt)
我用的是c++程序,自己去csdn搜教程就好了,记得安装的时候选要用的c++的选项。我们要用这个软件的goole test模板。
开始白盒测试吧
1.新建项目,用google test模板
左上角,文件—新建—项目
搜索模板,找到google test模板
主要就这三个框起来的文件要用。
给大家讲一下这些文件一般是怎么用的。
- 头文件 (.h 或 .hpp):
通常包含声明(declarations),即函数、类、变量的声明,但不包含具体的实现。
用于描述接口、结构、函数原型等。
通常以 .h 或 .hpp 为文件扩展名,例如 snake.h。
- 源文件 (.cpp):
包含实际的实现代码。
用于定义头文件中声明的函数和类的具体实现。
通常以 .cpp 为文件扩展名,例如 snake.cpp。
- #include 指令:
#include 指令用于在一个文件中包含另一个文件的内容。
在头文件中,通常使用 #include 来引入其他头文件,以便使用其他文件中声明的函数、类等。
在源文件中,使用 #include 来引入需要的头文件,确保在使用其他代码之前声明了相应的函数或类。
2.分别在pch.h , pch.cpp 里面把源文件拆分
pch.h放前面一些头文件和宏定义啥的,声明类。
pch.cpp放类的方法内容
test.cpp放测试函数(怎么写goole test的测试看这里看这里),很简单的啦。
可以用chatgpt,让它帮你写,不过会有一些报错,多半是说未分配空间,要自己malloc一下。
我直接拿我的程序打个样吧
pch.h
//
// pch.h
//
#pragma once
#include "gtest/gtest.h"
// 必要的头文件
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <windows.h>
#include <string.h>
using namespace std;
// 定义标记上下左右的明示常量
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
#define ESC 5
#define FOOD 10
// 定义表示位置的结构体类型
typedef struct snake {
int x;
int y;
struct snake* next;
} snake;
class game {
public:
// 定义全局变量
int score; // 当前得分
int speed; // 存储当前速度
int status;
snake* tail, * head; // 存储蛇头蛇尾
snake* food, * q;// q用于遍历链表
//HANDLE hOUT;
//函数
game() {
score = 0; // 当前得分
speed = 100; // 存储当前速度
status = RIGHT;
snake* tail = new snake;
snake* head = new snake; // 存储蛇头蛇尾
snake* food = new snake;
snake* q = new snake;// q用于遍历链表
/*
snake* tail = (snake*)malloc(sizeof(snake));
snake* head = (snake*)malloc(sizeof(snake)); // 存储蛇头蛇尾
snake* food = (snake*)malloc(sizeof(snake));
snake* q = (snake*)malloc(sizeof(snake));// q用于遍历链表
*/
}
void gotoxy(int x, int y); // 设置光标位置
int choice(void); // 载入游戏界面
int color(int c); // 设置字体颜色
void printGame(void); // 打印游戏界面
void printSnake(void); // 打印蛇身
void printFood(void); // 打印食物
void printTips(void); // 打印提示
void snakeMove(void); // 主操作函数
int biteSelf(void); // 判断是否咬到了自己
int encounterWall(void); // 判断是否撞墙
void keyboardControl(void); // 获取击键
void speedUp(void); // 加速
void speedDown(void); // 减速
int endGame(void); // 结束函数;
char* s_gets(char* st, int n); // 读取字符
void frees(snake*); // 释放内存
};
pch.cpp
//
// pch.cpp
//
#include "pch.h"
void game::gotoxy(int x, int y)
{
COORD c;
c.X = x;
c.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
int game::choice(void)
{
int yourchoice;
// 画出界面
gotoxy(35, 5);
color(11);
printf("\t贪吃蛇大作战\n");
printf("\n\n");
color(13);
printf("\t\t★★★★★★★★ Snake!");
printf("\t\t★★★★★★★★ Snake!");
gotoxy(25, 15);
color(12);
printf("1.进入游戏\t2.查看说明\t3.退出游戏\n");
color(11);
printf("请选择:");
scanf("%d", &yourchoice);
switch (yourchoice)
{
case 1:
system("cls");
// 初始化
printGame();
printSnake();
printFood();
break;
case 2:
system("cls");
printTips();
break;
case 3:
system("cls");
gotoxy(30, 10);
color(11);
printf("Bye!");
exit(0);
default:
system("cls");
printf("没有此序号,请输入1,2或3\n");
Sleep(2000);
system("cls");
}
return yourchoice;
}
int game::color(int c)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //更改文字颜色
return 0;
}
void game::printGame()
{
int i, j;
gotoxy(5, 5);
printf("游戏载入中...请稍后");
Sleep(2000);
system("cls");
// 打印上下界面
for (i = 0; i <= 50; i += 2)
{
gotoxy(i, 0);
printf("□");
gotoxy(i, 25);
printf("□");
}
// 打印左右界面
for (i = 0; i <= 25; i += 1)
{
gotoxy(0, i);
printf("□");
gotoxy(50, i);
printf("□");
}
// 打印中间网格
for (i = 1; i <= 24; i += 1)
{
for (j = 2; j <= 48; j += 2)
{
gotoxy(j, i);
color(11);
printf("■");
}
}
// 打印右侧的规则和计分栏
gotoxy(60, 13);
color(10);
printf("当前分数:%d分,当前速度%d", score, speed);
gotoxy(60, 15);
printf("用↑ ↓ ← →分别控制蛇的移动\n");
gotoxy(60, 18);
printf("每次获取食物加10分 按下F1加速,F2减速,空格暂停\n");
gotoxy(60, 20);
printf("不能撞墙和咬到自己!");
gotoxy(60, 22);
printf("速度不低于100,不高于300");
}
void game::printFood(void)
{
srand((unsigned)time(NULL)); // 利用时钟修改种子
food = (snake*)malloc(sizeof(snake));
food->x = 1; // 初始化x坐标
while (food->x % 2 && food->x)
{
food->x = rand() % 46 + 2;// 2-48
}
food->y = rand() % 23 + 1; // 1-24
q = head; // 不改变头遍历链表
while (q->next)
{
if (q->x == food->x && q->y == food->y)
{
free(food);
printFood();
}
else
{
gotoxy(food->x, food->y);
color(12);
printf("●");
break;
}
}
}
void game::printTips(void)
{
color(11);
printf("***********Tips************\n");
printf("1.采用合理的速度可以获得更高的分数哦!\n");
printf("2.一定不要撞到自己或者两边的墙!\n");
printf("3.游戏过程中按ESC退出游戏!\n");
}
void game::printSnake(void)
{
int i;
// 设定蛇尾(16,13),头插入,初始向右
tail = (snake*)malloc(sizeof(snake));
tail->x = 16;
tail->y = 13;
tail->next = NULL;
// 设定初始蛇长是4
for (i = 1; i <= 4; i++)
{
head = (snake*)malloc(sizeof(snake));
head->next = tail;
head->x = 16 + 2 * i;
head->y = 13;
tail = head; // 头成为尾
}
// 输出蛇身
while (tail->next)
{
gotoxy(tail->x, tail->y);
color(14);
printf("★");
tail = tail->next;
}
}
void game::snakeMove(void)
{
snake* snakenext;
snakenext = (snake*)malloc(sizeof(snake));
if (biteSelf())
{
gotoxy(60, 11);
printf("咬到自己啦!");
free(snakenext);
Sleep(1500);
system("cls");
exit(0);
}
else if (encounterWall())
{
gotoxy(60, 11);
printf("撞到墙啦!");
free(snakenext);
Sleep(1500);
system("cls");
exit(0);
}
else
{
// 前两个条件判断完成才开始移动
Sleep(350 - speed);
if (status == UP)
{
snakenext->x = head->x;
snakenext->y = head->y - 1;
snakenext->next = head;
head = snakenext;
q = head;
food= (snake*)malloc(sizeof(snake));
if (snakenext->x == food->x && snakenext->y == food->y)
{
while (q)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
score += FOOD;
gotoxy(60, 13);
printf("当前分数:%d分,当前速度%d", score, speed);
printFood();
}
else
{
while (q->next->next)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
gotoxy(q->next->x, q->next->y);
color(11);
printf("■");
free(q->next);
q->next = NULL;
}
}
else if (status == DOWN)
{
snakenext->x = head->x;
snakenext->y = head->y + 1;
snakenext->next = head;
head = snakenext;
q = head;
if (snakenext->x == food->x && snakenext->y == food->y)
{
while (q)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
score += FOOD;
gotoxy(60, 13);
printf("当前分数:%d分,当前速度%d", score, speed);
printFood();
}
else
{
while (q->next->next)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
gotoxy(q->next->x, q->next->y);
color(11);
printf("■");
free(q->next);
q->next = NULL;
}
}
else if (status == LEFT)
{
snakenext->x = head->x - 2;
snakenext->y = head->y;
snakenext->next = head;
head = snakenext;
q = head;
if (snakenext->x == food->x && snakenext->y == food->y)
{
while (q)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
score += FOOD;
gotoxy(60, 13);
printf("当前分数:%d分,当前速度%d", score, speed);
printFood();
}
else
{
while (q->next->next)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
gotoxy(q->next->x, q->next->y);
color(11);
printf("■");
free(q->next);
q->next = NULL;
}
}
else if (status == RIGHT)
{
snakenext->x = head->x + 2;
snakenext->y = head->y;
snakenext->next = head;
head = snakenext;
q = head;
if (snakenext->x == food->x && snakenext->y == food->y)
{
while (q)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
score += FOOD;
gotoxy(60, 13);
printf("当前分数:%d分,当前速度%d", score, speed);
printFood();
}
else
{
while (q->next->next)
{
gotoxy(q->x, q->y);
color(14);
printf("★");
q = q->next;
}
gotoxy(q->next->x, q->next->y);
color(11);
printf("■");
free(q->next);
q->next = NULL;
}
}
}
}
int game::biteSelf(void)
{
int x = 0; // 默认未咬到自己
q = head->next;
// 遍历蛇身
while (q)
{
if (q->x == head->x && q->y == head->y)
{
x = 1;
}
q = q->next;
}
return x;
}
int game::encounterWall(void)
{
int x = 0; // 默认未撞到墙
if (head->x == 0 || head->x == 50 || head->y == 0 || head->y == 25)
x = 1;
return x;
}
void game::keyboardControl(void)
{
status = RIGHT; // 初始蛇向右移动
while (1)
{
if (GetAsyncKeyState(VK_UP) && status != DOWN) // GetAsyncKeyState函数用来判断函数调用时指定虚拟键的状态
{
status = UP; //如果蛇不是向下前进的时候,按上键,执行向上前进操作
}
else if (GetAsyncKeyState(VK_DOWN) && status != UP) // 如果蛇不是向上前进的时候,按下键,执行向下前进操作
{
status = DOWN;
}
else if (GetAsyncKeyState(VK_LEFT) && status != RIGHT) // 如果蛇不是向右前进的时候,按左键,执行向左前进
{
status = LEFT;
}
else if (GetAsyncKeyState(VK_RIGHT) && status != LEFT) // 如果蛇不是向左前进的时候,按右键,执行向右前进
{
status = RIGHT;
}
if (GetAsyncKeyState(VK_SPACE))// 空格暂停
{
while (1)
{
Sleep(300);
if (GetAsyncKeyState(VK_SPACE)) // 再次按空格改变状态
{
break;
}
}
}
else if (GetAsyncKeyState(VK_ESCAPE))
{
status = ESC; // 按esc键,直接到结束界面
if (endGame())
{
Sleep(500);
system("cls");
break;
}
}
else if (GetAsyncKeyState(VK_F1)) // 按F1键,加速
{
speedUp();
gotoxy(60, 13);
printf("当前分数:%d分,当前速度%d", score, speed);
}
else if (GetAsyncKeyState(VK_F2)) // 按F2键,减速
{
speedDown();
gotoxy(60, 13);
printf("当前分数:%d分,当前速度%d", score, speed);
}
snakeMove();
}
}
void game::speedUp(void)
{
if (speed <= 280)
speed += 20;
}
void game::speedDown(void)
{
if (speed >= 120)
speed -= 20;
}
int game::endGame(void)
{
char x = 0;
char judge[5];
getchar();
gotoxy(60, 9);
printf("确定退出吗?(Yes/No)");
gotoxy(60, 11);
s_gets(judge, 5);
if (strcmp(judge, "Yes") == 0)
{
Sleep(250);
system("cls");
gotoxy(40, 11);
printf("\tBye!");
x = 1;
}
else
x = 0;
return x;
}
char* game::s_gets(char* st, int n)
{
char* ret_val;
char* find;
gotoxy(60, 11);
ret_val = fgets(st, n, stdin);
if (ret_val)
{
find = strchr(st, '\n');
if (find)
*find = '\0';
else
while (getchar() != '\n')
continue;
}
return ret_val;
}
void game::frees(snake* s)
{
snake* current = s;
while (current)
{
current = s;
s = current->next;
free(current);
}
}
test.cpp
#include "pch.h"
TEST(SnakeGameTest, printGame) {//打印游戏界面
game tcs;
EXPECT_NO_FATAL_FAILURE(tcs.printGame());
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n测试打印游戏界面结束,准备清空");
Sleep(2000);
system("cls");
}
TEST(SnakeGameTest, printTips) {//打印提示
game tcs;
// Assuming PrintTips doesn't have any return value, you may need to modify this based on your actual implementation.
EXPECT_NO_FATAL_FAILURE(tcs.printTips());
printf("\n\n\n\n\n\n\n测试打印提示结束,准备清空");
Sleep(2000);
system("cls");
}
TEST(SnakeGameTest, printSnake) {//打印蛇身
game tcs;
// Assuming PrintSnake doesn't have any return value, you may need to modify this based on your actual implementation.
EXPECT_NO_FATAL_FAILURE(tcs.printSnake());
printf("\n\n\n\n测试打印蛇身结束,准备清空");
Sleep(2000);
system("cls");
}
TEST(SnakeGameTest, snakeMove) {//测试上下左右移动
game tcs;
tcs.printGame();
int i;
// 设定蛇尾(16,13),头插入,初始向右 ,我们先初始化tail
tcs.tail = (snake*)malloc(sizeof(snake));
tcs.tail->x = 16;
tcs.tail->y = 13;
tcs.tail->next = NULL;
// 设定初始蛇长是4
for (i = 1; i <= 4; i++)
{
tcs.head = (snake*)malloc(sizeof(snake));
tcs.head->next = tcs.tail;
tcs.head->x = 16 + 2 * i;
tcs.head->y = 13;
tcs.tail = tcs.head; // 头成为尾
}
int initialX,initialY;
//上移动
tcs.status = UP;
initialY = tcs.head->y;
tcs.snakeMove();
EXPECT_EQ(tcs.head->y, initialY - 1);
//右移动
tcs.status = RIGHT;
initialX = tcs.head->x;
tcs.snakeMove();
EXPECT_EQ(tcs.head->x, initialX + 2);
// 下两个 ,避免吃到自己
tcs.status = DOWN;
initialY = tcs.head->y;
tcs.snakeMove();
tcs.snakeMove();
EXPECT_EQ(tcs.head->y, initialY +2);
//左移动
tcs.status = LEFT;
initialX = tcs.head->x;
tcs.snakeMove();
EXPECT_EQ(tcs.head->x, initialX -2);
tcs.color(15);
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n测试蛇身移动结束,准备清空");
Sleep(3000);
system("cls");
}
TEST(SnakeGameTest, speedUp) {
game tcs;
int initialSpeed = tcs.speed;
// 如果初始速度小于 280,调用加速函数
if (initialSpeed < 280) {
tcs.speedUp();
// 期望速度加了 20
EXPECT_EQ(initialSpeed + 20, tcs.speed);
}
else {
// 如果初始速度已经是 280 或更高,加速函数不会改变速度
EXPECT_EQ(initialSpeed, tcs.speed);
}
}
TEST(SnakeGameTest, speedDown) {
game tcs;
int initialSpeed = tcs.speed;
// 如果初始速度大于等于 120,调用减速函数
if (initialSpeed >= 120) {
tcs.speedDown();
// 期望速度减了 20
EXPECT_EQ(initialSpeed - 20, tcs.speed);
}
else {
// 如果初始速度小于 120,减速函数不会改变速度
EXPECT_EQ(initialSpeed, tcs.speed);
}
printf("\n\n\n\n\n\n\n\n测试移动速度结束,准备清空");
Sleep(2000);
system("cls");
}
TEST(SnakeGameTest, printFood) {
game tcs;
tcs.printGame();
// 调用蛇身生成函数
tcs.printSnake();
// 调用食物生成函数
tcs.printFood();
// 获取食物的坐标
int foodX = tcs.food->x;
int foodY = tcs.food->y;
// 食物坐标应在合法范围内
EXPECT_GE(foodX, 2);
EXPECT_LE(foodX, 48);
EXPECT_GE(foodY, 1);
EXPECT_LE(foodY, 24);
// 检查食物是否与蛇身重叠
snake* current = tcs.head;
while (current) {
EXPECT_FALSE(foodX == current->x && foodY == current->y);
current = current->next;
}
tcs.gotoxy(0, 30);
tcs.color(15);
printf("测试食物生成是否合理结束,准备清空");
Sleep(2000);
system("cls");
}
TEST(SnakeGameTest, GameEndsOnWallCollision) {
game tcs;
// 设置蛇头,模拟碰到墙壁
tcs.head= (snake*)malloc(sizeof(snake));
tcs.head->x = 50;
tcs.head->y = 10;
tcs.color(15);
// 调用游戏结束判定函数
EXPECT_TRUE(tcs.encounterWall());
}
TEST(SnakeGameTest, SnakeBitesItself) {
// 创建贪吃蛇实例
game tcs;
tcs.tail = (snake*)malloc(sizeof(snake));
tcs.tail->x = 16;
tcs.tail->y = 13;
tcs.tail->next = NULL;
;
int a[7] = { 16,16,14,14,14,16,16 };
int b[7] = { 13,12,12,13,14,14,13 };
for (int i = 1; i <= 6; i++) {
tcs.head = (snake*)malloc(sizeof(snake));
tcs.head->next = tcs.tail;
tcs.head->x = a[i];
tcs.head->y = b[i];
tcs.head->next = tcs.tail; // 新节点的next指向原来的蛇尾
tcs.tail = tcs.head; // 更新蛇尾为新节点
}
// 输出蛇身
int c = 1;
snake* show=(snake*)malloc(sizeof(snake));
while (tcs.tail->next)
{
tcs.gotoxy(tcs.tail->x, tcs.tail->y);
tcs.color(c++);
printf("★");
tcs.tail = tcs.tail->next;
}
tcs.color(15);
printf("\n\n\n\n\n\n\n\n");
bool isBite = tcs.biteSelf();
// 期望结果为 true,因为生成的蛇咬到了自己
EXPECT_TRUE(isBite);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
运行点这