51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 1519|回复: 0
打印 上一主题 下一主题

Selenium+Pytest自动化测试框架—禅道实战

[复制链接]
  • TA的每日心情
    无聊
    昨天 11:40
  • 签到天数: 943 天

    连续签到: 2 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2021-9-24 09:52:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
     前言
      有人问我登录携带登录的测试框架该怎么处理,今天就对框架做一点小升级吧,加入登录的测试功能。
      conftest.py更改
    1.  #!/usr/bin/env python3
    2.   # -*- coding:utf-8 -*-
    3.   import base64
    4.   import pytest
    5.   import allure
    6.   from py.xml import html
    7.   from selenium import webdriver
    8.   from page.webpage import WebPage
    9.   from common.readconfig import ini
    10.   from tools.send_mail import send_report
    11.   from tools.times import timestamp
    12.   from config.conf import cm
    13.   driver = None
    14.   @pytest.fixture(scope='session', autouse=True)
    15.   def drivers(request):
    16.       global driver
    17.       if driver is None:
    18.           driver = webdriver.Chrome()
    19.           web = WebPage(driver)
    20.           web.get_url(ini.url)
    21.       def fn():
    22.           driver.quit()
    23.       request.addfinalizer(fn)
    24.       return driver
    25.   @pytest.hookimpl(hookwrapper=True)
    26.   def pytest_runtest_makereport(item):
    27.       """
    28.       当测试失败的时候,自动截图,展示到html报告中
    29.       :param item:
    30.       """
    31.       pytest_html = item.config.pluginmanager.getplugin('html')
    32.       outcome = yield
    33.       report = outcome.get_result()
    34.       extra = getattr(report, 'extra', [])
    35.       if report.when == 'call' or report.when == "setup":
    36.           xfail = hasattr(report, 'wasxfail')
    37.           if (report.skipped and xfail) or (report.failed and not xfail):
    38.               screen_img = _capture_screenshot()
    39.               if screen_img:
    40.                   html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" ' \
    41.                          'onclick="window.open(this.src)" align="right"/></div>' % screen_img
    42.                   extra.append(pytest_html.extras.html(html))
    43.           report.extra = extra
    44.           report.description = str(item.function.__doc__)
    45.   def pytest_html_results_table_header(cells):
    46.       cells.insert(1, html.th('用例名称'))
    47.       cells.insert(2, html.th('Test_nodeid'))
    48.       cells.pop(2)
    49.   def pytest_html_results_table_row(report, cells):
    50.       cells.insert(1, html.td(report.description))
    51.       cells.insert(2, html.td(report.nodeid))
    52.       cells.pop(2)
    53.   def pytest_html_results_table_html(report, data):
    54.       if report.passed:
    55.           del data[:]
    56.           data.append(html.div('通过的用例未捕获日志输出.', class_='empty log'))
    57.   def pytest_html_report_title(report):
    58.       report.title = "pytest示例项目测试报告"
    59.   def pytest_configure(config):
    60.       config._metadata.clear()
    61.       config._metadata['测试项目'] = "测试百度官网搜索"
    62.       config._metadata['测试地址'] = ini.url
    63.   def pytest_html_results_summary(prefix, summary, postfix):
    64.       # prefix.clear() # 清空summary中的内容
    65.       prefix.extend([html.p("所属部门: XX公司测试部")])
    66.       prefix.extend([html.p("测试执行人: 随风挥手")])
    67.   def pytest_terminal_summary(terminalreporter, exitstatus, config):
    68.       """收集测试结果"""
    69.       result = {
    70.           "total": terminalreporter._numcollected,
    71.           'passed': len(terminalreporter.stats.get('passed', [])),
    72.           'failed': len(terminalreporter.stats.get('failed', [])),
    73.           'error': len(terminalreporter.stats.get('error', [])),
    74.           'skipped': len(terminalreporter.stats.get('skipped', [])),
    75.           # terminalreporter._sessionstarttime 会话开始时间
    76.           'total times': timestamp() - terminalreporter._sessionstarttime
    77.       }
    78.       print(result)
    79.       if result['failed'] or result['error']:
    80.           send_report()
    81.   def _capture_screenshot():
    82.       """截图保存为base64"""
    83.       now_time, screen_path = cm.screen_file
    84.       driver.save_screenshot(screen_path)
    85.       allure.attach.file(screen_path, "测试失败截图...{}".format(
    86.           now_time), allure.attachment_type.PNG)
    87.       with open(screen_path, 'rb') as f:
    88.           imagebase64 = base64.b64encode(f.read())
    89.       return imagebase64.decode()
    复制代码
    config.ini更改
    1. [HOST]
    2.   HOST = http://127.0.0.1/zentao/user-login-L3plbnRhby9teS5odG1s.html
    复制代码
    conf.py更改

    1. <strong> </strong>#!/usr/bin/env python3
    2.   # -*- coding:utf-8 -*-
    3.   import os
    4.   from selenium.webdriver.common.by import By
    5.   from tools.times import datetime_strftime
    6.   class ConfigManager(object):
    7.       # 项目目录
    8.       BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    9.       # 日志目录
    10.       LOG_PATH = os.path.join(BASE_DIR, 'logs')
    11.       # 报告目录
    12.       REPORT_PATH = os.path.join(BASE_DIR, 'report', 'report.html')
    13.       ELEMENT_PATH = os.path.join(BASE_DIR, 'page_element')
    14.       # 元素定位的类型
    15.       LOCATE_MODE = {
    16.           'css': By.CSS_SELECTOR,
    17.           'xpath': By.XPATH,
    18.           'name': By.NAME,
    19.           'id': By.ID,
    20.           'class': By.CLASS_NAME
    21.       }
    22.       # 邮件信息
    23.       EMAIL_INFO = {
    24.           'username': '1084502012@qq.com',  # 切换成你自己的地址
    25.           'password': 'QQ邮箱授权码',
    26.           'smtp_host': 'smtp.qq.com',
    27.           'smtp_port': 465
    28.       }
    29.       # 收件人
    30.       ADDRESSEE = [
    31.           '1084502012@qq.com',
    32.       ]
    33.       @property
    34.       def ini_file(self):
    35.           # 配置文件
    36.           _file = os.path.join(self.BASE_DIR, 'config', 'config.ini')
    37.           if not os.path.exists(_file):
    38.               raise FileNotFoundError("配置文件%s不存在!" % _file)
    39.           return _file
    40.       def element_file(self, name):
    41.           """页面元素文件"""
    42.           element_path = os.path.join(self.ELEMENT_PATH, '%s.yaml' % name)
    43.           if not os.path.exists(element_path):
    44.               raise FileNotFoundError("%s 文件不存在!" % element_path)
    45.           return element_path
    46.       @property
    47.       def log_path(self):
    48.           log_path = os.path.join(self.BASE_DIR, 'logs')
    49.           if not os.path.exists(log_path):
    50.               os.makedirs(log_path)
    51.           return os.path.join(log_path, "%s.log" % datetime_strftime())
    52.       @property
    53.       def screen_file(self):
    54.           now_time = datetime_strftime("%Y%m%d%H%M%S")
    55.           # 截图目录
    56.           screenshot_dir = os.path.join(self.BASE_DIR, 'screen_capture')
    57.           if not os.path.exists(screenshot_dir):
    58.               os.makedirs(screenshot_dir)
    59.           screen_path = os.path.join(screenshot_dir, "{}.png".format(now_time))
    60.           return now_time, screen_path
    61.   cm = ConfigManager()
    62.   if __name__ == '__main__':
    63.       print(cm.BASE_DIR)
    复制代码

    page更改
      添加了几个函数!
    1. #!/usr/bin/env python3
    2.   # -*- coding:utf-8 -*-
    3.   """
    4.   selenium基类
    5.   本文件存放了selenium基类的封装方法
    6.   """
    7.   from selenium.webdriver.support import expected_conditions as EC
    8.   from selenium.webdriver.support.ui import WebDriverWait
    9.   from selenium.common.exceptions import TimeoutException, NoSuchElementException
    10.   from config.conf import cm
    11.   from tools.times import sleep
    12.   from tools.logger import Logger
    13.   log = Logger(__name__).logger
    14.   class WebPage(object):
    15.       """selenium基类"""
    16.       def __init__(self, driver):
    17.           # self.driver = webdriver.Chrome()
    18.           self.driver = driver
    19.           self.timeout = 20
    20.           self.wait = WebDriverWait(self.driver, self.timeout)
    21.       def get_url(self, url):
    22.           """打开网址并验证"""
    23.           self.driver.maximize_window()
    24.           self.driver.set_page_load_timeout(60)
    25.           try:
    26.               self.driver.get(url)
    27.               self.driver.implicitly_wait(10)
    28.               log.info("打开网页:%s" % url)
    29.           except TimeoutException:
    30.               raise TimeoutException("打开%s超时请检查网络或网址服务器" % url)
    31.       @staticmethod
    32.       def element_locator(func, locator):
    33.           """元素定位器"""
    34.           name, value = locator
    35.           return func(cm.LOCATE_MODE[name], value)
    36.       def find_element(self, locator):
    37.           """寻找单个元素"""
    38.           return WebPage.element_locator(lambda *args: self.wait.until(
    39.               EC.presence_of_element_located(args)), locator)
    40.       def find_elements(self, locator):
    41.           """查找多个相同的元素"""
    42.           return WebPage.element_locator(lambda *args: self.wait.until(
    43.               EC.presence_of_all_elements_located(args)), locator)
    44.       def focus(self):
    45.           """聚焦元素"""
    46.           self.driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")
    47.       def elements_num(self, locator):
    48.           """获取相同元素的个数"""
    49.           number = len(self.find_elements(locator))
    50.           log.info("相同元素:{}".format((locator, number)))
    51.           return number
    52.       def input_text(self, locator, txt):
    53.           """输入(输入前先清空)"""
    54.           sleep(0.5)
    55.           ele = self.find_element(locator)
    56.           ele.clear()
    57.           ele.send_keys(txt)
    58.           log.info("输入文本:{}".format(txt))
    59.       def is_click(self, locator):
    60.           """点击"""
    61.           ele = self.find_element(locator)
    62.           ele.click()
    63.           sleep()
    64.           log.info("点击元素:{}".format(locator))
    65.       def is_exists(self, locator):
    66.           """元素是否存在(DOM)"""
    67.           try:
    68.               WebPage.element_locator(lambda *args: EC.presence_of_element_located(args)(self.driver), locator)
    69.               return True
    70.           except NoSuchElementException:
    71.               return False
    72.       def alert_exists(self):
    73.           """判断弹框是否出现,并返回弹框的文字"""
    74.           alert = EC.alert_is_present()(self.driver)
    75.           if alert:
    76.               text = alert.text
    77.               log.info("Alert弹窗提示为:%s" % text)
    78.               alert.accept()
    79.               return text
    80.           else:
    81.               log.error("没有Alert弹窗提示!")
    82.       def element_text(self, locator):
    83.           """获取当前的text"""
    84.           _text = self.find_element(locator).text
    85.           log.info("获取文本:{}".format(_text))
    86.           return _text
    87.       def get_attribute(self, locator, name):
    88.           """获取元素属性"""
    89.           return self.find_element(locator).get_attribute(name)
    90.       @property
    91.       def get_source(self):
    92.           """获取页面源代码"""
    93.           return self.driver.page_source
    94.       def refresh(self):
    95.           """刷新页面F5"""
    96.           self.driver.refresh()
    97.           self.driver.implicitly_wait(30)
    98.   if __name__ == "__main__":
    99.       pass
    复制代码
    page_element更改
    1. 账号: "css==input[name=account]"
    2.   密码: "css==input[name=password]"
    3.   登录: "css==button#submit"
    4.   我的地盘: "xpath==//nav[@id='navbar']//span[text()=' 我的地盘']"
    5.   右上角名称: "css==.user-name"
    6.   退出登录: "xpath==//a[text()='退出']"
    复制代码
    1.  产品按钮: "xpath==//nav[@id='navbar']//a[text()='产品']"
    2.   添加产品: "xpath==//div[@id='pageActions']//a[text()=' 添加产品']"
    3.   产品名称: "css==#name"
    4.   产品代号: "css==#code"
    5.   保存产品: "css==#submit"
    6.   产品列表: "xpath==//ul[@class='nav nav-stacked nav-secondary scrollbar-hover']//a[1]"
    复制代码

    page_object更改
    1.  #!/usr/bin/env python3
    2.   # -*- coding: utf-8 -*-
    3.   from page.webpage import WebPage
    4.   from common.readelement import Element
    5.   login = Element('login')
    6.   class LoginPage(WebPage):
    7.       """登录类"""
    8.       def username(self, name):
    9.           """用户名"""
    10.           self.input_text(login['账号'], name)
    11.       def password(self, pwd):
    12.           """密码"""
    13.           self.input_text(login['密码'], pwd)
    14.       def submit(self):
    15.           """登录"""
    16.           self.is_click(login['登录'])
    17.       def quit_login(self):
    18.           """退出登录"""
    19.           self.is_click(login['右上角名称'])
    20.           self.is_click(login['退出登录'])
    21.       def login_success(self):
    22.           """验证登录"""
    23.           return self.is_exists(login['我的地盘'])
    复制代码
    1. #!/usr/bin/env python3
    2.   # -*- coding:utf-8 -*-
    3.   from page.webpage import WebPage, sleep
    4.   from common.readelement import Element
    5.   product = Element('product')
    6.   class ProductPage(WebPage):
    7.       """产品类"""
    8.       def click_product(self):
    9.           """点击产品"""
    10.           self.is_click(product['产品按钮'])
    11.       def add_product(self):
    12.           """添加产品"""
    13.           self.is_click(product['添加产品'])
    14.       def add_product_content(self, name, code):
    15.           """添加产品内容"""
    16.           self.input_text(product['产品名称'], name)
    17.           self.input_text(product['产品代号'], code)
    18.       def save_product(self):
    19.           """保存产品"""
    20.           self.focus()
    21.           self.is_click(product['保存产品'])
    22.       def product_list(self):
    23.           """产品列表"""
    24.           return [i.get_attribute('title') for i in self.find_elements(product['产品列表'])]
    25.   if __name__ == '__main__':
    26.       a = product['产品列表'][1] + "[1]"
    27.       print(a)
    复制代码
     TestCase更改
    1.  #!/usr/bin/env python3
    2.   # -*- coding: utf-8 -*-
    3.   import pytest
    4.   from tools.times import sleep
    5.   from page_object.loginpage import LoginPage
    6.   class TestLogin:
    7.       """测试登录"""
    8.       @pytest.mark.parametrize("name,pwd", [('admin', 'Admin123456'), ('test', 'test123')])
    9.       def test_001(self, drivers, name, pwd):
    10.           login = LoginPage(drivers)
    11.           login.username(name)
    12.           login.password(pwd)
    13.           login.submit()
    14.           sleep(3)
    15.           res = login.alert_exists()
    16.           if res:
    17.               assert res == "登录失败,请检查您的用户名或密码是否填写正确。"
    18.           elif login.login_success():
    19.               login.quit_login()
    复制代码
    1.  #!/usr/bin/env python3
    2.   # -*- coding:utf-8 -*-
    3.   import pytest
    4.   import allure
    5.   from random import randint
    6.   from tools.times import sleep
    7.   from page_object.loginpage import LoginPage
    8.   from page_object.productpage import ProductPage
    9.   @allure.feature("测试产品模块")
    10.   class TestProduct:
    11.       @pytest.fixture(scope='class', autouse=True)
    12.       def is_login(self, request, drivers):
    13.           login = LoginPage(drivers)
    14.           login.username('admin')
    15.           login.password('Admin123456')
    16.           login.submit()
    17.           sleep(3)
    18.           def logout():
    19.               login.quit_login()
    20.           request.addfinalizer(logout)
    21.       @allure.story("测试添加产品")
    22.       def test_001(self, drivers):
    23.           """搜索"""
    24.           product = ProductPage(drivers)
    25.           product.click_product()
    26.           product.add_product()
    27.           name, code = randint(100, 999), randint(100, 999)
    28.           product.add_product_content(name, code)
    29.           product.save_product()
    30.           sleep(3)
    31.           product.click_product()
    32.           assert str(name) in product.product_list()
    33.   if __name__ == '__main__':
    34.       pytest.main(['TestCase/test_aproduct.py'])
    复制代码

    测试结果
      登录之后的测试用例:



    测试登录的用例:




    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏1
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-5-8 07:43 , Processed in 0.066801 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表