目录
一、项目背景
1.1、项目起源
1.2、市场需求
1.3、项目目标
二、项目功能
2.1 用户管理功能
2.2 游戏对战功能
三、测试报告
3.1.功能测试
编辑
3.1.1注册功能测试
解决bug:
测试总结:
3.1.2登录功能测试
测试总结:
3.1.3.匹配和落子测试
3.2.界面测试
bug:
测试总结
3.3.性能测试编辑
总体测试框架:
3.3.1.利用BeanShell解决乱码问题
3.3.3.添加CSV格式的用户信息
3.3.4.添加同步定时器
3.3.5.创建梯度压测线程组
3.3.6.生成测试报告
TIP:
3.4.自动化测试
3.4.1.测试框架:
3.4.2.utils工具类的实现
3.4.3.注册界面自动化测试
3.4.4.登录界面自动化测试
3.4.5.游戏大厅界面自动化测试
3.4.6.集成测试:
3.4.7.测试总结:
自动化测试盲点难点解决方法:
1.注意弹窗只能是用强制等待或者是显示等待!、
2.隐式等待为什么要写在utils文件中,不能写在后面的文件里面吗
四.总结:
一、项目背景
1.1、项目起源
随着互联网的飞速发展和普及,网络娱乐方式日益多样化。五子棋作为一种传统而经典的棋类游戏,深受广大棋迷的喜爱。然而,传统的五子棋游戏方式往往受限于地点和时间,无法满足人们随时随地进行游戏的需求。因此,开发一款网页版五子棋项目,旨在打破这种限制,让棋迷们能够随时随地通过网络享受五子棋的乐趣。
1.2、市场需求
-
便捷性需求:现代人生活节奏加快,对于娱乐方式的需求也更加注重便捷性。网页版五子棋无需下载和安装,只需打开浏览器即可进行游戏,极大地方便了用户。
-
社交性需求:网络游戏的社交属性日益凸显,玩家们不仅追求游戏本身的乐趣,还希望在游戏中结交新朋友。网页版五子棋可以提供在线对战功能,满足玩家的社交需求。
-
普及性需求:五子棋作为一种传统文化,其普及和传承具有重要意义。网页版五子棋项目的开发,有助于将这一传统文化以更现代化的方式呈现给更多人,推动五子棋的普及和发展。
1.3、项目目标
-
开发一款易用的网页版五子棋游戏:游戏界面简洁明了,操作方便快捷,让玩家能够轻松上手并享受游戏乐趣。
-
实现在线对战功能:支持玩家在线匹配对手,进行实时对战,增强游戏的互动性和趣味性。
-
优化游戏性能和用户体验:通过不断优化游戏算法和界面设计,提高游戏的运行速度和稳定性,为玩家提供更加流畅、舒适的游戏体验。
二、项目功能
2.1 用户管理功能
用户注册与登录:用户可以通过注册页面注册账号,填写用户名、密码等信息进行注册,注册成功后可在登录页面输入账号密码登录。
用户信息管理:系统能够记录用户的相关信息,如用户id、用户名、密码、天梯分数、排位总场次和胜场总场次等,并提供修改密码、查看个人信息等功能。
用户状态管理:通过session管理模块实现用户登录状态的维护,使用户在游戏过程中保持登录状态,直到用户主动退出或超时自动退出。
2.2 游戏对战功能
匹配对战:系统根据用户的天梯分数等信息进行匹配,为玩家找到合适的对手,进入游戏房间进行对战。
实时对战:在游戏房间内,玩家可以进行实时的五子棋对战,双方轮流落子,通过点击棋盘上的空位放置棋子。
胜负判断:系统能够实时检测当前落子位置在横、纵、斜四个方向是否有连续五个相同的棋子,以判断胜负。
三、测试报告
全部的测试用例:
3.1.功能测试
3.1.1注册功能测试
注册已经存在的用户:
与预期结果一致,注册失败!并显示用户名以及被占用。
输入的账号或密码超出规定的长度范围:
drop database if exists gobang;
create database if not exists gobang;
use gobang;
create table if not exists user(id int primary key auto_increment,username varchar(32) unique key not null,password varchar(128) not null,score int,total_count int,win_count int
);
根据MySQL定义的数据表中账号的最多长度是32位,而密码是128位,当我输入的账号超出32位时,系统就会报错,与预期结果一致!
异常注册的情况:
账号为空时:
跟预期结果一致,注册失败!
密码为空时:
我们发现当密码为空时居然注册成功了!这明显跟我们预期不符合的,并且我们之前在定义数据表的时候,password明显是有not null属性的,这是为什么呢?!
账号密码都为空时
我勒个豆,竟然也注册成功了!但是我们已经注册账号为空的时候,下一次再注册账号为空会有用户名已经存在的现象。
解决bug:
问题是在于前端给后端传的数据是 空字符串, jsonvalue的 isNull函数判断有点问题。目前是把条件修改了,判断前端传递过来的是不是一个空串。 这个是后端在处理的时候,提前就要判断处理掉的。
有一个疑问是密码为什么是空,数据库还能插入成功。
是因为前端给后端的是一个空字符串, 并且密码经过md5加密后,并不为空,所以插入是没问题的。
其次是name为何不写,也能注册成功。是因为不写,向后端传的是空字符串,而不是null。
所以,第一个要解决的是,我们用户不写用户名和密码的时候,后端要及时能够检测出来。所以修改了判断条件,得以解决。 第二个疑问是为何不写,sql语句还能执行成功,是由于 空字符串 和 null是有区别的!
当我们在数据库中插入空字符串的账号和密码的时候,插入成功!这也就验证了空字符串和null的区别,也解释了之前账号和密码为空的时候能注册成功的原因。
当我们更改判断条件之后,再来用空账号,密码注册一下(此时已经将数据库中的空账号的用户删除了)
此时,就不能用空账号进行注册了!bug解决完毕!
正常注册:
发现成功跳转到登录页面
与预期结果一致!
测试总结:
通过对注册页面的界面测试和功能测试,得出以下结论:
注册页面的背景图片显示正常,页面中的文字样式,和注册框均能正常显示。除此之外,注册框中的字样、输入框和“提交”按钮也能够正常显示。
正常注册情况,在注册一个并没有注册过的用户名时,可以正常注册成功,并且页面会提示用户“注册成功!
当注册已经注册过的用户时,页面会显示用户名被占用,让用户重新注册!
但是当密码为空时,用户也注册成功了!发现了这个bug。
3.1.2登录功能测试
正确的用户名和密码登录:
登录成功,与预期结果一致!并且账号和密码都能复制粘贴,密码也是隐藏了
异常的用户名和密码登录:
界面会显示用户名密码错误,与预期结果一致!
当跳过登录界面,直接进入游戏房间
预期:找不到用户信息,请重新登录,并跳转到登录界面
测试总结:
通过对登录页面的界面测试和功能测试,得出以下结论:
登录页面的背景图片显示正常,页面中的文字样式,和登录框均能正常显示。
建议:
但当登录失败时,可以具体向用户展示到底是账号错误还是密码错误,便于用户修改
3.1.3.匹配和落子测试
这里匹配和落子测试和界面一起测试
3.2.界面测试
bug:
当用户名字很长的时候 界面就会出现这种重回覆盖的情况
解决措施:
用户名过长最少有两种方式,第一种就是直接截取,比如你的用户名有19位,我只展示10位就行了。剩下的不展示
第二种就是,19位,只展示一部分,当鼠标放上去之后,展示完整的用户名
这里实现了第二种
测试总结
通过测试游戏大厅页面的界面显示和匹配功能,得出以下结论:
游戏大厅页面的左上角“五子棋对战”字样正常显示,背景图片正常显示
玩家信息(用户名、分数、比赛场次、获胜场次)正常显示,
匹配功能正常,两个同级别玩家点击匹配按钮后,会匹配在一起。
并且当有一方五星连珠时,界面会显示游戏胜利/失败
通过对游戏大厅页面的界面测试和功能测试,并未在游戏大厅页面发现bug。
3.3.性能测试

下面我来使用 JMeter 对五子棋项目的登录接口和获取用户信息接口进行简单的性能测试,下面就来介绍五子棋项目性能测试的一个测试流程。
总体测试框架:
3.3.1.利用BeanShell解决乱码问题
3.3.2.添加保存cookie信息
因为用户在登录之后,我们需要从info接口读取消息,所以需要自动保存用户登录的cookie信息
这里直接选择standard即可
3.3.3.添加CSV格式的用户信息
我们为了更加真实的模拟用户登录场景,我们在CSV文件中添加了用户信息以供模拟真实的用户登录场景。
下面的是配置,用username和password表示账号密码,后续直接在body中引用即可。
如下面的登录接口就调用了CSV文件中的账号和密码。
3.3.4.添加同步定时器
我们为了保证线程是并发执行的,我们添加了同步定时器。
3.3.5.创建梯度压测线程组
这里我们创建一个梯度压测线程组(Stepping Thread Group),来慢慢增大我们对这两个接口的并发请求的数量,以达到性能测试的目的。
3.3.6.生成测试报告
我们发现有一小部分测试用例出现的bug,进行具体分析:
发现都是网络超时,这跟网络情况相关,或者我的服务器稳定性比较差~
TIP:
JMeter从CSV读取数据并不只适用于数据存放在URL参数里面。
- 不仅限于URL参数:虽然CSV数据常用于URL参数化,但JMeter也支持将CSV中的数据用于其他类型的请求参数,如POST请求的body、请求头等。
我们现在做的就是将数据存放在body中。
3.4.自动化测试
3.4.1.测试框架:
在common这个软件包中主要存放的是utils这个工具类,这个工具类里面具体实现了创建浏览器对象和截图工具,避免每次测试都需要写一份工具,避免了代码的冗余。
在images这个文件夹中存放的是每次截图生成的图片,并且以天为单位生成小的文件夹,每份文件夹又有用时间进行格式化和生成该图片的类名。
在tests这个软件包中存放的是各个页面的自动化测试代码,并且RunTest文件进行总体的一个测试。
3.4.2.utils工具类的实现
import os
import sys
import datetimefrom selenium import webdriver
from selenium.webdriver.ie.service import Service
from webdriver_manager.chrome import ChromeDriverManager#创建一个浏览器对象class Driver:driver = ""def __init__(self):options = webdriver.ChromeOptions()self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=options)def getScreeShot(self):# 创建屏幕截图# 图片文件名称:./2024-05-08-173456.pngdirname = datetime.datetime.now().strftime("%Y-%m-%d")# 判断dirname文件夹是否已经存在,若不存在则创建文件夹# ../images/2024-05-08if not os.path.exists("../images/" + dirname):os.mkdir("../images/" + dirname)# 2024-05-08-173456.png# 图片路径:../images/调用方法-2024-05-08/2024-05-08-173456.png# 图片路径:../images/LoginSucTest-2024-05-08/2024-05-08-173456.png# 图片路径:../images/LoginFailTest-2024-05-08/2024-05-08-173456.pngfilename = sys._getframe().f_back.f_code.co_name + "-" + datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S") + ".png"self.driver.save_screenshot("../images/" + dirname + "/" + filename)
BlogDriver = Driver()
其中截图的工具类别出心裁:
-
sys._getframe().f_back.f_code.co_name
通过调用栈获取调用该代码的函数名,作为文件名的一部分。这样可以动态地根据函数名为截图命名。 -
datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S")
获取当前时间,并将其格式化为“年-月-日-时分秒”的形式,作为文件名中的时间戳部分。 -
filename
将函数名和时间戳拼接成一个完整的文件名,后缀为“.png”。 -
self.driver.save_screenshot("../images/" + dirname + "/" + filename)
使用 Selenium 的save_screenshot
方法,将当前浏览器窗口的截图保存到指定路径中。路径由../images/
、dirname
(某个子目录名)以及生成的文件名组成。
3.4.3.注册界面自动化测试
代码:
#测试注册界面
import time
from distutils.command.register import registerfrom selenium.webdriver.common.by import Byfrom common.Utils import BlogDriverclass GobangRegister:url = ""driver = ""def __init__(self):self.url = "http://123.249.125.60:8085/register.html"self.driver = BlogDriver.driverself.driver.get(self.url)#成功注册的测试用例def RegisterSucTest(self):self.driver.find_element(By.CSS_SELECTOR,"#user_name").send_keys("lisi9")self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")self.driver.find_element(By.CSS_SELECTOR,"#submit").click()#强制等待time.sleep(3)#此时需要点击弹窗里面的确定,才能跳转页面alert = self.driver.switch_to.alertalert.accept()#能够找到登录界面中的登录二字,说明注册成功,否则注册失败self.driver.find_element(By.CSS_SELECTOR,"body > div.login-container > div > h3")#异常注册的测试用例def RegisterFailTest(self):#若连续多次的send_keys会出现关键词的拼接,而不是替换。若要替换需要先clearself.driver.find_element(By.CSS_SELECTOR,"#user_name").clear()self.driver.find_element(By.CSS_SELECTOR,"#password").clear()self.driver.find_element(By.CSS_SELECTOR,"#user_name").send_keys("lisi")self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")self.driver.find_element(By.CSS_SELECTOR,"#submit").click()time.sleep(3)#self.driver.quit()_register = GobangRegister()
_register.RegisterSucTest()
#_register.RegisterFailTest()
3.4.4.登录界面自动化测试
#测试登录页面
import timefrom common.Utils import BlogDriver
from selenium.webdriver.common.by import Byclass GobangLogin:url = ''driver = ''def __init__(self):self.url = "http://123.249.125.60:8085/login.html"self.driver = BlogDriver.driverself.driver.get(self.url)#登录失败的测试用例def LoginFailTest(self):#隐式等待#self.driver.implicitly_wait(3)self.driver.find_element(By.CSS_SELECTOR,'#user_name').send_keys('lisi8')self.driver.find_element(By.CSS_SELECTOR,'#password').send_keys('123456')self.driver.find_element(By.CSS_SELECTOR,'#submit').click()time.sleep(3)#此时需要点击弹窗里面的确定,才能跳转页面alert = self.driver.switch_to.alertalert.accept()time.sleep(3)#成功登录的测试用例def LoginSucTest(self):self.driver.find_element(By.CSS_SELECTOR,'#user_name').send_keys('lisi')self.driver.find_element(By.CSS_SELECTOR,'#password').send_keys('123456')self.driver.find_element(By.CSS_SELECTOR,'#submit').click()time.sleep(3)#此时需要点击弹窗里面的确定,才能跳转页面alert = self.driver.switch_to.alertalert.accept()time.sleep(3)# self.driver.find_element(By.CSS_SELECTOR,'#match-button')# #截图# BlogDriver.getScreeShot()# #self.driver.quit()# #强制等待# time.sleep(3)# #此时需要点击弹窗里面的确定,才能跳转页面# alert = self.driver.switch_to.alert# alert.accept()# time.sleep(3)#time.sleep(3)#此时需要点击弹窗里面的确定,才能跳转页面alert = self.driver.switch_to.alertalert.accept()time.sleep(3)# _login = GobangLogin()
# # _login.LoginFailTest()
# _login.LoginSucTest()
3.4.5.游戏大厅界面自动化测试
#测试游戏大厅页面
import timefrom common.Utils import BlogDriver
from selenium.webdriver.common.by import Byclass GobangHall:url = ' 'driver = ' 'def __init__(self):self.url = "http://123.249.125.60:8085/game_hall.html"self.driver = BlogDriver.driverself.driver.get(self.url)#测试游戏大厅匹配和停止匹配按钮def HallTest(self):time.sleep(3)#此时需要点击弹窗里面的确定,才能跳转页面alert = self.driver.switch_to.alertalert.accept()#点击开始匹配self.driver.find_element(By.CSS_SELECTOR,'#match-button').click()#点击停止匹配self.driver.find_element(By.CSS_SELECTOR,'#match-button').click()time.sleep(3)#重新匹配self.driver.find_element(By.CSS_SELECTOR,'#match-button').click()#截图BlogDriver.getScreeShot()
3.4.6.集成测试:
#from tests import GobangRegister
from tests import GobangLogin
from tests import GobangHall
from common.Utils import BlogDriverif __name__ == "__main__":#游戏注册页面#GobangRegister.GobangRegister().RegisterSucTest()#GobangLogin.GobangLogin().LoginFailTest()#登陆成功之后就可以调用博客首页测试首页的用例(登陆状态)#最终一定是登录状态GobangLogin.GobangLogin().LoginSucTest()#游戏大厅界面GobangHall.GobangHall().HallTest()#指定浏览器的退出BlogDriver.driver.quit()
3.4.7.测试总结:
如上图运行过程所示,我们的自动化测试用例全部通过,当我们执行失败的时候需要截图进行纠错。
自动化测试盲点难点解决方法:
1.注意弹窗只能是用强制等待或者是显示等待!、
为什么?
在Selenium自动化测试中,当遇到弹窗时,通常使用的等待方式主要是显式等待。这是因为弹窗的出现往往是由某些用户操作或页面事件触发的,而这些事件可能不是立即发生的,因此需要等待弹窗确实出现后再进行相应的操作。
注意在前面文件中不需要我们自己退出,我们只用在最后的RunTest一次退出即可,不然前面程序已经退出,将会运行失败
2.隐式等待为什么要写在utils文件中,不能写在后面的文件里面吗
解答:
#点击完成之后出现页面的跳转,页面跳转需要加载时间,可能会出现代码执行的速度比页面渲染的速度要快,导致元素查找不到,因此可以添加等待
#添加隐式等待和显示等待都可以,任选择一个
#隐式等待:创建浏览器对象之后就可以加上,因为隐式等待的作用域在driver整个生命周期
#显示等待:可以作用在当前代码中
四.总结:
我们一共进行了功能测试、自动化测试以及性能测试这三种测试方式,在功能测试阶段,我们着重点是测试玩家双方进行五子棋对弈时的过程;在自动化测试阶段,我们主要测试各个页面的相关信息,着重测试了登录与注册的功能;在性能测试阶段,我们着重测试了用户登录与获取用户信息的接口性能,并生成了性能测试报告。
在这期间,发现了不少bug,例如用户注册时,没有账号和密码也能注册成功;当用户的名称过长时,会对前端界面按钮造成覆盖等等。但最终都解决了,不得不说,测试真的很有意思~~~