Python自动化Discuz登录实例(selenium+unittest+ddt)

一、项目背景

对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、去执行脚本,查看测试报告和测试截图

 

天道酬勤

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

three996

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值