2024年最详细的web自动化测试框架(从封装开始)

打印 上一主题 下一主题

主题 1060|帖子 1060|积分 3180

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本文利用Page Objects设计模式,Page Objects模式是Selenium中的一种测试设计模式,主要是将每一个页面设计为一个Class,此中包含页面中需要测试的元素(按钮,输入框,标题 等),这样在页面中可以通过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变革时,在UI级的自动化测试中,对象设计模式体现测试正在交互的web应用,程序用户界面中的一个区域,这个是淘汰了代码的重复,也就是说,如果用户界面发生了改变,只需要在一个地方修改程序就可以了。
PO 模式的设计思想与面向对象相似,能让测试代码变得可读性更好,可维护性高,复用性高。

PO 模式可以把一个页面分为三个层级:对象库层、操作层、业务层。

  • 对象库层:封装定位元素的方法。
  • 操作层:封装对元素的操作。
  • 业务层:将一个或多个操作组合起来完成一个业务功能。

安装 selenium 包
安装 parameterized 包添加 HTMLTestRunner
一、base:底子层,主要编写底层定位元素的类,它是一个包。
二、page:对象层,编写具体的业务逻辑,把页面每一个操作举动单独的写一个方法或者是函数。
三、test:测试层,内里主要是测试模块,也可以说是每个测试的场景的代码。
四、run:运行层:整个自动化测试的运行目次。
五、report:测试报告目次,主要用来存放测试报告。
六、data:存储测试利用到测试数据。
七、config:配置文件存储目次。
八、common:公共类,内里编写公共利用到的方法。
九、utils:工具类,存放工具,如文件处理、说明文档等。
页面临象设计模式

一、Base底子层

在该层中主要编写了底子代码。在该层主要定义了类WebUI,在这个类中编写了单个元素和多个元素定位的方法。
新建Base项目,在该项目的下面新建base_page.py,这个类主要是用来将selenium的操作进行二次封装,方便page层的项目调用。
  1. from telnetlib import EC
  2. from selenium.webdriver.support.select import Select
  3. from selenium.webdriver.support.wait import WebDriverWait
  4. class BasePage:
  5.     def __int__(self,driver):
  6.         self.driver = driver
  7.     # 打开页面
  8.     def into_testin(self, url):
  9.         self.driver.get(url)
  10.     # 定位元素
  11.     def locator_element(self,loc):
  12.         return self.driver.find_element(*loc)
  13.     # 定位一组元素
  14.     def locate_eles(self, loc):
  15.         return self.driver.find_elements(*loc)
  16.     # 设置值的关键字
  17.     def send_keys(self, loc, value):
  18.         self.locator_element(loc).send_keys(value)
  19.     # 点击的关键字操作
  20.     def click(self, loc):
  21.         self.locator_element(loc).click()
  22.     # 清理输入框
  23.     def clear(self, loc):
  24.         self.locator_element(loc).clear()
  25.     # 封装选中下拉框关键字
  26.     def choice_select(self, loc, value):
  27.         sel = Select(self.locator_element(loc))
  28.         sel.select_by_value(value)
  29.     # 智能等待
  30.     def wait_for_element(self, locator, timeout=10):
  31.         """
  32.         等待元素出现
  33.         :param locator: 定位器元组,例如 (By.ID, 'element_id')
  34.         :param timeout: 超时时间(秒),默认为10秒
  35.         :return: 找到的WebElement对象
  36.         """
  37.         try:
  38.             element = WebDriverWait(self.driver, timeout).until(
  39.                 EC.presence_of_element_located(locator)
  40.             )
  41.             return element
  42.         except Exception as e:
  43.             print(f"等待元素时出错: {e}")
  44.             return None
复制代码
在Base该项目的下面新建base_util.py,由于我们每次都要用到 Chrome 的驱动,因此我们可以把它封装成一个工具类:
  1. from selenium import webdriver
  2. import unittest
  3. class BaseUtil(unittest.TestCase):
  4.    
  5.     # 在每个测试用例执行之前都会调用。它用于设置测试环境。
  6.     def setUp(self) -> None:
  7.         # 声明一个全局变量driver,这样在类的其他方法中也可以访问这个变量。
  8.         global driver
  9.         # 创建一个Chrome浏览器实例,并将其赋值给self.driver,这样所有测试用例都可以使用这个浏览器实例。
  10.         self.driver = webdriver.Chrome()
  11.         driver = self.driver
  12.         driver.get('https://www.luffycity.com/')
  13.         # 设置隐式等待时间为5秒,这意味着在查找页面元素时,Selenium会等待最多5秒。
  14.         self.driver.implicitly_wait(5)
  15.         self.driver.maximize_window()
  16.     # 在每个测试用例执行之后都会调用。它用于清理测试环境。当前这个方法是空的,没有执行任何操作。
  17.     def tearDown(self) -> None:
  18.         pass
复制代码
二、page对象层

在这一层的类直接继承了底子层的类,以类属性的方法指明每个操作元素属性的值,然后依据操作步骤编写对应的方法。
比如关于登录的操作:输入用户名、输入密码,点击登录,获取文本的信息操作会在实例中实现的登录操作,然后把每个登录操作封装成一个方法,这样实现登录测试用例直接调用。
  1. from base.base_page import BasePage
  2. from selenium.webdriver.common.by import By
  3. class LoginPage(BasePage):
  4.     zhuce = (By.XPATH,'//*[@id="__layout"]/div/header/div/div/div[2]/div[2]/span')
  5.     shouji = (By.XPATH,'//*[@id="__layout"]/div/div[2]/div/div/div[1]/div[1]/input')
  6.     mima = (By.XPATH,'//*[@id="__layout"]/div/div[2]/div/div/div[1]/div[2]/input')
  7.     denglu = (By.XPATH,'//*[@id="__layout"]/div/div[2]/div/div/button')
  8.     def __init__(self, driver):
  9.         self.driver = driver
  10.     def loginpage(self,username,password):
  11.         self.click(LoginPage.zhuce)
  12.         self.send_keys(LoginPage.shouji,username)
  13.         self.send_keys(LoginPage.mima,password)
  14.         self.click(LoginPage.denglu)
复制代码
其他page类,样例:
  1. from base.base_page import BasePage
  2. from selenium.webdriver.common.by import By
  3. class BlackClassPage(BasePage):
  4.     blackclass = (By.XPATH,'//*[@id="__layout"]/div/header/div/div/nav/a[3]/li')
  5.     cesiclass = (By.XPATH,"/html/body/div[3]/div/div/div[1]/main/div[2]/div[1]/div/article/span[12]")
  6.     cesifirsrclass = (By.XPATH,'//*[@id="__layout"]/div/div[1]/main/div[2]/div[2]/div[1]/a/dl/dd/div[3]/button')
  7.     def blackclasspage(self):
  8.         self.wait_for_element(BlackClassPage.blackclass)
  9.         self.click(BlackClassPage.blackclass)
  10.         self.click(BlackClassPage.cesiclass)
  11.         self.click(BlackClassPage.cesifirsrclass)
复制代码
三、test测试层

在这里首先需要导入对象层中的类和unittest单位测试框架,在测试类中,继承了unittest.TestCase和对象层中的类,TestCase是由于在编写自动化测试的用例中,用到的测试固件、测试断言和测试执行都是需要它中的方法,而对象层中的类包含对象层中的测试操作步骤的方法,继承后可以直接进行调用。
留意:
在编写用例的时候需要添加备注信息,明白体现该用例是测试的哪个点,验证的哪个场景;
测试模块都是以test_开头,测试方法也是以test_开头的;
在test层下创建test_login类,实例代码如下:
  1. from time import sleep
  2. from base.base_util import BaseUtil
  3. from pageobject.login import LoginPage
  4. class TestLogin(BaseUtil):
  5.     def test_login(self):
  6.         '''
  7.         登录路非学堂
  8.         :return:
  9.         '''
  10.         lf = LoginPage(self.driver)
  11.         lf.loginpage("13375383415","Hao20001018")
  12.         sleep(3)
复制代码
四、run运行层

运行层,主要是运行测试用例的目次,我们可以根据测试模块来运行,也可以运行全部的模块,该层的内容也适用于全部场景(适用的前提是po设计模式的目次结构如上所示)
  1. import unittest
  2. from HTMLTestRunner_cn import HTMLTestRunner
  3. import time
  4. if __name__ == '__main__':
  5.     suite = unittest.defaultTestLoader.discover("testcase", "test_login.py")
  6.     now = time.strftime("%Y-%m-%d %H_%M_%S")  # 报告生成时间
  7.     test_reports_address = './report'  # 测试报告存放位置
  8.     filename = './report/' + now + 'report.html'  # 设置报告文件名
  9.     fp = open(filename, 'wb')
  10.     # 生成一个htmlTestRunenr运行对象
  11.     runner = HTMLTestRunner(stream=fp, title="路非学堂自动化测试报告", description="报告详情如下")
  12.     runner.run(suite)
  13.     fp.close()
  14.     # # 向指定邮箱发送测试报告的html文件
  15.     # time.sleep(3)
  16.     # # 查找最新生成的测试报告地址
  17.     # new_report_addr = SendEmail().acquire_report_address(test_reports_address)
  18.     # # 自动发送邮件
  19.     # SendEmail().send_email(new_report_addr)
  20.     # 测试用例均使用"test_"开头命名
  21.     # suites = unittest.defaultTestLoader.discover(dirpath, 'test_*.py', top_level_dir=dirpath)
  22.     # for suite in suites:
  23.     #     test_cases.addTests(suite)
  24.     # return test_cases
  25.     # 生成html报告文件
  26.     # report_file = open("./report/report.html", "wb")
  27.     # 通过运行器运行测试用例
  28.     # runner.run(suite)
复制代码
五、report测试报告目次

主要用于存放测试报告(利用HTMLTestRunner天生测试报告)

 执行完测试自动发送邮件(经尝试可用)
  1. import os
  2. import smtplib
  3. from email.mime.text import MIMEText
  4. from email.mime.multipart import MIMEMultipart
  5. class SendEmail(object):
  6.     def __init__(self, username, passwd, recv, title, content,
  7.                  file_path=None, ssl=False,
  8.                  email_host='smtp.163.com', port=25, ssl_port=465):
  9.         self.username = username  # 用户名
  10.         self.passwd = passwd  # 密码
  11.         self.recv = recv  # 收件人,多个要传list ['a@qq.com','b@qq.com]
  12.         self.title = title  # 邮件标题
  13.         self.content = content  # 邮件正文
  14.         self.file_path = file_path  # 附件路径,如果不在当前目录下,要写绝对路径
  15.         self.email_host = email_host  # smtp服务器地址
  16.         self.port = port  # 普通端口
  17.         self.ssl = ssl  # 是否安全链接
  18.         self.ssl_port = ssl_port  # 安全链接端口
  19.         # 发送邮件
  20.     def send_email(self):
  21.         msg = MIMEMultipart()
  22.         msg.attach(MIMEText(self.content))  # 邮件正文的内容
  23.         # 构造附件
  24.         for f_path, file_dirs, files in os.walk(self.file_path):
  25.             for file in files:
  26.                 msg.attach(self._att_html(os.path.join(f_path, file)))
  27.         msg['Subject'] = self.title  # 邮件主题
  28.         msg['From'] = self.username  # 发送者账号
  29.         msg['To'] = ','.join(self.recv)  # 接收者账号列表
  30.         if self.ssl:
  31.             self.smtp = smtplib.SMTP_SSL(self.email_host, port=self.ssl_port)
  32.         else:
  33.             self.smtp = smtplib.SMTP(self.email_host, port=self.port)
  34.         # 发送邮件服务器的对象
  35.         self.smtp.login(self.username, self.passwd)
  36.         try:
  37.             self.smtp.sendmail(self.username, self.recv, msg.as_string())
  38.             pass
  39.         except Exception as e:
  40.             print('出错了。。', e)
  41.         else:
  42.             print('发送成功!')
  43.         self.smtp.quit()
  44.     # 构造邮件附件
  45.     @ staticmethod
  46.     def _att_html(filename):
  47.         # 构造附件
  48.         atthtml = MIMEText(open(filename, 'rb').read(), 'base64',
  49.             'utf-8')  # 文件放在同一路径,不放在同一路径改一下比如'D:/test/report.html
  50.         atthtml["Content-Type"] = 'application/octet-stream'
  51.         atthtml["Content-Disposition"] = 'attachment;filename = "GAD_Smoke_report.html"'
  52.         return atthtml
  53. # 调用并发送邮件
  54. if __name__ == '__main__':
  55.     m = SendEmail(
  56.     username = 'cicada_luo@163.com',  # 这里填发送者邮箱
  57.     passwd = 'TCBXOAOF...',   # 授权码还是什么忘记了
  58.     recv = ['1761885773@qq.com'],  # 接收者邮箱
  59.     title = 'G——smoke',
  60.     content = 'G——smoke测试报告',
  61.     file_path = 'D:\\G_webUI\\report',  # 发送的文件
  62.     email_host = 'smtp.163.com',
  63.     ssl_port =465,
  64.     ssl = True,
  65.     )
  66.     m.send_email()    # 发送邮件
复制代码
六、data:存储测试

存放数据文件
七、config配置文件目次

放置HTMLTestRunner等包。
常见题目

1、元素定位不到怎么办

体现情势为:程序抛出 NoSuchElementException 非常
解决思绪:
① 检查元素定位属性值是否写错,很多时候错误都是因为粗心导致的。
② 添加期待。
有时,程序执行过快,导致程序已经执行完了,而元素还未加载出来,那么就会抛出非常。(我们添加期待时间即可。最粗暴的做法就是 sleep(3)—强制期待3秒,这样做使得程序运行时间较长,一般少用。最常用的是利用显示期待(搭配 until()方法、expected_conditions 类来利用))
  1. def find_element_p(self, *args):
  2.     try:
  3.         return WebDriverWait(self.driver, 5, 0.5).until(EC.presence_of_element_located(*args))
  4.     except (NoSuchElementException, TimeoutException):
  5.         print("超过元素定位等待时长,无法获取到该元素,请检查定位路径")
复制代码
③ 以上方法不行时,那么就再尝试利用其他方式进行元素定位(常见的元素定位方式但是有八种之多)
④ 当利用xpath定位时,有大概,定位元素之进步行了某些操作,但程序逻辑没有进行这些操作,那么就大概导致定位元素的xpath路径不同等,从而导致定位元素失败。如某些元素需要点击才能出现,但你的脚本程序未进行点击操作,天然就不大概定位得到该元素了。
2、元素无法交互


体现情势为:程序抛出非常:ElementNotInteractableException: Message: element not interactable
解决思绪
① 检查进行交互的元素是否唯一,元素不唯一时也会出现此类错误
② 检查元素是否被隐藏。如果元素被隐藏起来,也无法进行交互。常见案例有:某按钮需要鼠标悬停在该元素上才能进行交互操作,此时就需要用到 ActionChains 类。例:
  1. # 鼠标移动到指定元素上
  2. def move_element(self, element_xp):
  3.     move = self.find_element_p((By.XPATH, element_xp))
  4.     ActionChains(self.driver).move_to_element(move).perform()
复制代码
③检查元素是否被其他元素遮挡。当元素被其他元素所遮掩,那么也无法对该元素进行操作。解决办法就是移除其他元素的遮掩(通过脚本操作遮掩元素移开被遮掩的元素)。 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

西河刘卡车医

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表