一、项目背景
对Discuz论坛网站进行登录测试
二、功能实现
1、自动运行用例
2、自动生成测试报告
3、自动断言与截图
三、项目架构
四、思路与实现
1、先做一个登录的线性脚本,定位好元素,确保用例能执行
from time import sleep # 从time模块中导入sleep方法
from selenium import webdriver # 从selenium模块中导入webdriver
url = "http://192.168.152.128/upload/forum.php" # 定义url地址
driver = webdriver.Chrome() # 定义对象
driver.get(url=url) # 访问url
driver.find_element_by_id('ls_username').send_keys('admin') # 输入用户名(id定位)
sleep(3)
driver.find_element_by_id('ls_password').send_keys('123456') # 输入密码(id定位)
sleep(3)
driver.find_element_by_xpath('//*[@id="lsform"]/div/div/table/tbody/tr[2]/td[3]/button/em').click() # 点击登录(xpath定位)
sleep(3)
driver.find_element_by_link_text('退出').click() # 点击退出(link定位)
sleep(3)
driver.quit() # 退出
2、当我们需要执行多条测试用例时,像打开网页,网页最大化,点击退出等操作会有大量的重复,显得代码冗余且开发、维护成本高,我们可以导入unittest模块,封装环境准备与销毁(本次只是测试登录,等到测试读帖、发帖、回复等操作时,可以封装登录函数,明白其原理即可)
import unittest
from selenium import webdriver
from time import sleep
class UpDown(unittest.TestCase): # 环境准备
def setUp(self) -> None:
self.driver = webdriver.Chrome()
self.driver.get(url='http://192.168.152.128/upload/forum.php')
self.driver.maximize_window()
sleep(3)
def tearDown(self) -> None: # 环境销毁
self.driver.close()
self.driver.quit()
3、修改线性脚本,添加多条测试用例,因为打开网页等重复操作都封装了,我们只需要写测试用例脚本,登录步骤都是相同的,只是输入数据不一样,直接复制脚本,单纯修改用户名和密码,同时,我们加上断言,unittest框架中提供了多种断言方式,我们在点击登录操作后,添加断言,判断登录是否成功
from time import sleep
import unittest
from updown import UpDown
class Testlogin(UpDown):
def testlogin1(self): # 测试用例
'''登录(正向):用户名、密码全正确'''
self.driver.find_element_by_id('ls_username').send_keys('admin') # 输入用户名(id定位)
self.driver.find_element_by_id('ls_password').send_keys('123456') # 输入密码(id定位)
self.driver.find_element_by_xpath(
'//*[@id="lsform"]/div/div/table/tbody/tr[2]/td[3]/button/em').click() # 点击登录(xpath定位)
sleep(3)
self.str = self.driver.find_element_by_link_text('退出').text # 找到退出按钮所在位置的文本
self.assertEqual(self.str, '退出') # 添加断言,判断找到的文本是不是‘退出’
self.driver.find_element_by_link_text('退出').click() # 点击退出(link定位)
sleep(3)
def testlogin2(self): # 测试用例
'''登录(反向):用户名为空、密码正确'''
self.driver.find_element_by_id('ls_username').send_keys('') # 输入用户名(id定位)
self.driver.find_element_by_id('ls_password').send_keys('123456') # 输入密码(id定位)
self.driver.find_element_by_xpath(
'//*[@id="lsform"]/div/div/table/tbody/tr[2]/td[3]/button/em').click() # 点击登录(xpath定位)
sleep(3)
self.str = self.driver.find_element_by_link_text('退出').text # 找到退出按钮所在位置的文本
self.assertEqual(self.str, '退出') # 添加断言,判断找到的文本是不是‘退出’
self.driver.find_element_by_link_text('退出').click() # 点击退出(link定位)
sleep(3)
def testlogin3(self): # 测试用例
'''登录(反向):用户名正确、密码为空'''
self.driver.find_element_by_id('ls_username').send_keys('admin') # 输入用户名(id定位)
self.driver.find_element_by_id('ls_password').send_keys('') # 输入密码(id定位)
self.driver.find_element_by_xpath(
'//*[@id="lsform"]/div/div/table/tbody/tr[2]/td[3]/button/em').click() # 点击登录(xpath定位)
sleep(3)
self.str = self.driver.find_element_by_link_text('退出').text # 找到退出按钮所在位置的文本
self.assertEqual(self.str, '退出') # 添加断言,判断找到的文本是不是‘退出’
self.driver.find_element_by_link_text('退出').click() # 点击退出(link定位)
sleep(3)
def testlogin4(self): # 测试用例
'''登录(反向):用户名、密码全为空'''
self.driver.find_element_by_id('ls_username').send_keys('') # 输入用户名(id定位)
self.driver.find_element_by_id('ls_password').send_keys('') # 输入密码(id定位)
self.driver.find_element_by_xpath(
'//*[@id="lsform"]/div/div/table/tbody/tr[2]/td[3]/button/em').click() # 点击登录(xpath定位)
sleep(3)
self.str = self.driver.find_element_by_link_text('退出').text # 找到退出按钮所在位置的文本
self.assertEqual(self.str, '退出') # 添加断言,判断找到的文本是不是‘退出’
self.driver.find_element_by_link_text('退出').click() # 点击退出(link定位)
sleep(3)
if __name__ == '__main__': # 调试脚本
unittest.main()
4、执行脚本生成报告,看看效果,unittest框架中提供了用例组织与执行,同时会生成日志与测试结果
import unittest
from BSTestRunner import BSTestRunner
import time
# 定义测试用例路径
test_dir = './test_case'
discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')
if __name__ == '__main__':
# 存放测试报告的文件夹
report_dir = './test_report/report'
# 将测试报告的命名时间格式化
now = time.strftime('%Y-%m-%d %H_%M_%S')
# 测试报告文件的完成路径
report_name = report_dir + '/' + now + ' result.html'
# 打开文件在报告文件中写入测试结果
with open(report_name, 'wb') as f:
runner = BSTestRunner(stream=f, title='Test Report', description='Test case result')
runner.run(discover)
5、通过上述步骤已经能够执行少量脚本,当测试用例数量过多时,我们的脚本代码量还是太冗余,仔细观察不同的测试用例脚本,发现只有数据不同,其余代码均相同,可以通过ddt来实现数据驱动,本质就是数据与脚本的分离,我们只需要添加与删除数据来控制自动化用例的个数,不需要重复大量的编写脚本
①先准备一个csv文件,存放需要参数化的数据,所有同一行的数据,用英文逗号隔开,第一行放参数,下面放与参数对应的数据,数据可以为空,空值也要用逗号隔开
username,password,id,mark
test01,123456,1,用户名密码全正确
test02,123456,2,用户名密码全正确
,123456,3,用户名为空
test03,,4,密码为空
,,5,用户名密码全为空
②写好csv文件后,我们需要获取csv文件中的数据,封装成函数
import csv
# 获取csv中的data数据
def get_csv_data(file_path): # 通过csv路径得到csv数据
data_list = [] # 定义空列表
with open(file_path, mode='r', encoding='utf-8') as f: # 根据路径打开文件
csv_reader = csv.reader(f) # 读取csv文件,赋值给csv_reader
next(csv_reader) # 从第二行数据开始
for line in csv_reader: # 遍历csv_reader
data_list.append(line) # 将csv_reader中的每条数据都添加到data_list列表中
return data_list # 返回data_list列表
if __name__ == '__main__': # 调试脚本
file_path = r'D:\App\Pycharm\PyCharm Community Edition 2019.2\Projects\test09\Discuz_unittest_ddt\csv_data\login_data.csv'
a = get_csv_data(file_path)
print(a)
③当用例不通过时,我们想还原当时场景时,就需要截图功能函数了
from selenium import webdriver
import os
# 自定义工具模块 Screenshot.py
# 自定义截图函数 insert_img()
def insert_img(driver, filename):
# 获取当前模块所在路径
func_dir = os.path.dirname(__file__)
# 获取上级路径 test_case
base_dir = os.path.dirname(func_dir)
# 将路径转化为字符串
base_dir = str(base_dir)
# 对路径的字符串进行替换
base_dir = base_dir.replace("\\", "/")
# 定截图存放路径
file_path = base_dir + "./test_report/screenshot/" + filename
driver.get_screenshot_as_file(file_path)
if __name__ == '__main__': # 调试脚本
driver = webdriver.Chrome()
driver.get("https://www.baidu.com/")
insert_img(driver, "baidu.png")
driver.quit()
6、修改测试用例脚本,导入ddt,导入截图方法和获取csv数据方法,主要是ddt装饰器对类和方法的装饰,具体看代码
from time import sleep
import unittest
from updown import UpDown
from ddt import ddt, data, unpack # 导入ddt
from tool.Screenshot import insert_img # 导入截图方法
from tool.get_csv_data import get_csv_data # 导入获取csv数据的方法
# 定义csv数据路径
file_path = r'D:\App\Pycharm\PyCharm Community Edition 2019.2\Projects\test09\Discuz_unittest_ddt\csv_data\login_data.csv'
get_csv_data(file_path) # 调用函数,获取csv中的data数据
@ddt # 装饰测试类
class Testlogin(UpDown):
@data(*get_csv_data(file_path)) # 装饰测试方法,csv中有几条数据,就执行几条测试用例
@unpack # 拆分数据
def test01(self, username, password, id, mark): # 测试用例
self.driver.find_element_by_id('ls_username').send_keys(username) # 进行参数化用户名
self.driver.find_element_by_id('ls_password').send_keys(password) # 进行参数化密码
self.driver.find_element_by_xpath(
'//*[@id="lsform"]/div/div/table/tbody/tr[2]/td[3]/button/em').click() # 点击登录(xpath定位)
sleep(3)
insert_img(self.driver, '截图' + id + '.png') # 调用函数,截取登录后界面
self.str = self.driver.find_element_by_link_text('退出').text
self.assertEqual(self.str, '退出')
self.driver.find_element_by_link_text('退出').click() # 点击退出(link定位)
sleep(3)
if __name__ == '__main__':#调试脚本
unittest.main()
7、去执行脚本,查看测试报告和测试截图