windows下python版web自动化测试框架(完整搭建流程引导)

https://blog.csdn.net/weixin_45912307/article/details/108955085

windows下python版web自动化测试框架 (完整搭建流程引导)

    • 一、项目环境准备工作
    • 二、 创建项目(以pycharm为例)
    • 三、框架的封装
        • 1. 创建框架目录
        • 2. 公共层
        • 3. 输出层
        • 4. 页面元素定位层
        • 5. 页面对象层(操作方法)
        • 6. 测试用例层
        • 7. 测试数据层
        • 8. readme.md 帮助文件
        • 9. requirements.txt 项目依赖工具包
        • 10.run.py 项目运行文件
    • 四、与Jenkins集成

一、项目环境准备工作

1.配置虚拟环境, 请参考之前一篇文章:https://blog.csdn.net/weixin_45912307/article/details/108347547

2. 创建项目虚拟环境
mkvirtualenv -p python3 虚拟环境名称
windows下python版web自动化测试框架(完整搭建流程引导)_第1张图片

二、 创建项目(以pycharm为例)

1. 点击 new project
windows下python版web自动化测试框架(完整搭建流程引导)_第2张图片
2. 先找到刚刚创建好的虚拟环境目录
windows下python版web自动化测试框架(完整搭建流程引导)_第3张图片
windows下python版web自动化测试框架(完整搭建流程引导)_第4张图片
windows下python版web自动化测试框架(完整搭建流程引导)_第5张图片
windows下python版web自动化测试框架(完整搭建流程引导)_第6张图片

3. 创建工程目录
windows下python版web自动化测试框架(完整搭建流程引导)_第7张图片

windows下python版web自动化测试框架(完整搭建流程引导)_第8张图片
windows下python版web自动化测试框架(完整搭建流程引导)_第9张图片
4. 选择好虚拟环境,选择好工程目录,点击创建
windows下python版web自动化测试框架(完整搭建流程引导)_第10张图片
5. 创建好的工程目录架构
windows下python版web自动化测试框架(完整搭建流程引导)_第11张图片

三、框架的封装

1. 创建框架目录

windows下python版web自动化测试框架(完整搭建流程引导)_第12张图片
最终框架结构如图:
windows下python版web自动化测试框架(完整搭建流程引导)_第13张图片

2. 公共层

2.1 base_page.py 页面公共函数

# —— coding :utf-8 ——
# @time:    2020/10/7 20:58
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:   [email protected]
# @File:    base_page.py
import logging
import datetime

from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys

from Common.file_path import screenshots_path
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import win32gui
import win32con
# pip install pywin32

class BasePage:
    def __init__(self, driver):
        self.driver = driver

    # 1. 等待元素可见
    def wait_eleVisible(self, locator, timeout=20, poll_frequency=0.5, doc=""):
        """
        :param locator: 元素定位,元组形式。 (元素类型,元素定位方式)
        :param timeout:
        :param poll_frequency:
        :param doc: 模块名称_页面名称_操作名称
        :return:
        """
        logging.info('等待元素可见:{}可见'.format(locator))
        try:
            # 开始等待时间
            start_time = datetime.datetime.now()
            WebDriverWait(self.driver, timeout, poll_frequency).until(EC.visibility_of_element_located(locator))
            # 结束等待时间
            end_time = datetime.datetime.now()
            # 等待时长,转换为s
            wait_times = (end_time - start_time).seconds
            logging.info(
                "{0}:元素:{1}已可见,等待起始时间:{2},等待结束时间:{3},等待时间:{4}".format(doc, locator, start_time, end_time, wait_times))
        except:
            # 铺获异常到日志中
            # 截图--保存到指定目录
            logging.exception('等待元素可见失败!')
            self.save_screenshot(doc)
            raise

    # 2. 判断元素是否存在
    def ele_isExist(self, locator, timeout=10, doc=""):
        logging.info("{}中是否存在元素:{}".format(doc, locator))
        try:
            WebDriverWait(self.driver, timeout).until(EC.visibility_of_element_located(locator))
            logging.info("{0}秒内页面{1}存在中存在元素:{2}".format(timeout, doc, locator))
            return True
        except:
            logging.exception("{0}秒内页面{1}存在中不存在元素:{2}".format(timeout, doc, locator))
            return False

    # 3. 查找元素
    def find_element(self, locator, doc=""):
        logging.info('{0}查找元素:{1}'.format(doc, locator))
        try:
            return self.driver.find_element(*locator)
        except:
            logging.exception('查找元素失败')
            self.save_screenshot(doc)
            raise
            # 3. 判断元素是否存在

    # 4. 点击操作
    def click_element(self, locator, doc):
        ele = self.find_element(locator, doc)
        logging.info('{0}点击元素:{1}'.format(doc, locator))
        try:
            ele.click()
            logging.info('{0}点击元素:{1}成功'.format(doc, locator))
        except:
            logging.exception('元素点击操作失败!')
            self.save_screenshot(doc)
            raise

    # 5. 输入操作
    def input_text(self, locator, content, doc=""):
        ele = self.find_element(locator, doc)
        logging.info('{0}元素:{1}输入文本'.format(doc, locator))
        try:
            logging.info('{0}元素:{1},输入内容为:{2}'.format(doc, locator, content))
            ele.send_keys(content)
        except:
            logging.exception('{0}元素:{1}输入失败'.format(doc, locator))
            self.save_screenshot(doc)
            raise

    # 6. 获取元素文本
    def get_element_text(self, locator, text, doc=""):
        ele = self.find_element(locator, doc)
        logging.info('{0}获取元素:{1}的文本内容'.format(doc, locator))
        try:
            text = ele.text
            logging.info('{0}获取元素:{1}的文本内容为:{2}'.format(doc, locator, text))
            return text
        except:
            logging.exception('获取元素:{}文本内容失败'.format(locator))
            self.save_screenshot(doc)
            raise

    # 7. 获取元素属性
    def get_element_attribute(self, locator, attr, doc=""):
        ele = self.find_element(locator, doc)
        logging.info('{0}获取元素:{1}的属性:{2}'.format(doc, locator, attr))
        try:
            ele_attr = ele.get_attribute(attr)
            logging.info('{0}元素:{1}的属性:{2}值为:{3}'.format(doc, locator, attr, ele_attr))
            return ele_attr
        except:
            logging.exception('{}获取元素:{}的属性失败'.format(doc, locator))
            self.save_screenshot(doc)
            raise

    # 8. alert处理
    def switch_to_alert(self, timeout=30, poll_frequency=0.5, action='accept', doc=""):
        logging.info('{0}_切换alert弹框'.format(doc))
        try:
            WebDriverWait(self.driver, timeout, poll_frequency).until(EC.alert_is_present())
            alert = self.driver.switch_to.alert
            value = alert.text
            logging.info('{0}当前弹框内容为:{1}'.format(doc, value))
            if action == 'accept':
                alert.accept()
            elif action == 'dismiss':
                alert.dismiss()
            return value
        except:
            logging.exception('{}弹框操作失败!'.format(doc))
            self.save_screenshot(doc)
            raise

    # 9. 保存截图
    def save_screenshot(self, doc):
        # 图片名称:模块名_页面名称_操作名称_年-月-日_时分秒.png
        # filePath = "截图路径" + "{0}".format(time.strftime("%Y%m%d%H%M%S"))
        filePath = screenshots_path + "{0}_{1}.png".format(doc, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
        try:
            self.driver.save_screenshot(filePath)
            logging.info("截取网页成功,文件路径为:{}".format(filePath))
        except:
            logging.exception('保存网页失败!')
            raise

    # 10. 滚动条处理
    def scrollbal_handle(self,url,mode='foot',doc=""):
        global js
        logging.info('{}进行滚动条操作'.format(doc))
        try:
            self.driver.get(url)
            if mode=='top':
                # 滚动到顶部
                js = "window.scrollTo(0,0)"
                self.driver.execute_script(js)
            else:
                # 滚动到底部
                js = "window.scrollTo(0,document.body.scrollHeight)"
                self.driver.execute_script(js)
            logging.info('{}滚动成功'.format(doc))
        except:
            logging.exception('{}滚动操作失败'.format(doc))
            self.save_screenshot(doc)
            raise

    # 11. 上传操作
    def upload_file(self, UpfilePath,doc=""):
        logging.info('{}进行文件上传'.format(doc))
        try:
            dialog = win32gui.FindWindow('#32770', '打开')  # 一级
            # 二级窗口
            ComBoxEx32 = win32gui.FindWindowEx(dialog, 0, 'ComBoxEx32', None)
            # 三级窗口
            ComboBox = win32gui.FindWindowEx(ComBoxEx32, 0, 'ComboBox', None)
            # 四级窗口
            edit = win32gui.FindWindowEx(ComboBox, 0, 'Edit', None)
            button = win32gui.FindWindowEx(dialog, 0, 'Button', "打开(&0)")

            # 操作
            # 输入文件地址
            win32gui.SendMessage(edit, win32con.WM_SETTEXT, None, UpfilePath)  # 发送文件路径
            # 打开文件按钮
            win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)  # 点击打开按钮
            logging.info('{}文件上传成功'.format(doc))
        except:
            logging.exception('{}文件上传操作失败'.format(doc))
            self.save_screenshot(doc)
            raise

    # 12. iframe切换
    def switch_to_frame(self,locator,doc=""):
        ele = self.find_element(locator)
        logging.info('{}表单切换'.format(doc))
        try:
            self.driver.switch_to.frame(ele)
            logging.info('{}切换表单成功'.format(doc))
        except:
            logging.exception('{}切换表单失败!'.format(doc))
            self.save_screenshot(doc)
            raise

    # 13. 窗口切换
    def switch_to_window(self, window_reference, timeout=30, poll_frequency=0.5, window_handles=None, doc=""):
        logging.info("{0}_切换窗口".format(doc))
        try:
            if window_reference == "new":
                if window_handles:
                    WebDriverWait(timeout, poll_frequency).until(EC.new_window_is_opened(window_handles))
                    current_window_handles = self.driver.window_handles
                    self.driver.switch_to.window(current_window_handles[-1])
                else:
                    logging.exception("打开新窗口时,请传入window_handles参数")
                    raise ("打开新窗口时,请传入window_handles参数")
            elif window_reference == "default":
                self.driver.switch_to.default_content()
            else:
                self.driver.switch_to.window(window_reference)
            logging.info("{0}_切换窗口成功".format(doc))
        except:
            logging.exception("{0}_切换窗口失败".format(doc))
            self.save_screenshot(doc)
            raise

    # 14. 执行js
    def execute_script(self, js_str, element_info=None,doc=""):
        logging.info('{}执行js'.format(doc))
        try:
            if element_info:
                self.driver.execute_script(js_str)
            else:
                self.driver.execute_script(js_str, None)
            logging.info('{}执行js成功'.format(doc))
        except:
            logging.exception('{}执行js操作失败'.format(doc))
            self.save_screenshot(doc)
            raise

    # 15. 鼠标事件
    # 元素鼠标操作:右击-测试OK
    def context_click(self, locator):
        mouse = ActionChains(self.driver)
        ele = self.find_element(self,locator)
        logging.info('{}元素进行右击操作'.format(locator))
        mouse.context_click(ele).perform()
    # 元素鼠标操作:移动到该元素上--测试OK
    def move_to_element_by_mouse(self, locator):
        ele = self.find_element(locator)
        mouse = ActionChains(self.driver)
        logging.info('将鼠标移动到{}元素上'.format(locator))
        mouse.move_to_element(ele).perform()
    # 长按元素
    def long_press_element(self, locator, seconds):
        ele = self.find_element(locator)
        logging.info('将鼠标长按到{}元素上后松开'.format(locator))
        mouse = ActionChains(self.driver)
        mouse.click_and_hold(ele).pause(seconds).release(ele)

    def scrollIntoView(self, locator):
        ele = self.find_element(locator)
        logging.info('将滚动条滚动至{}元素可见'.format(locator))
        self.driver.execute_script('arguments[0].scrollIntoView();', ele)
        time.sleep(1)

    # 16. 键盘事件
    # 删除内容-测试OK
    def back_space(self, locator):
        ele = self.find_element(locator)
        logging.info('{0}元素操作back_space'.format(locator))
        ele.send_keys(Keys.BACK_SPACE)
    # 清空内容--测试OK
    def clear_input(self,locator):
        ele = self.find_element(locator)
        logging.info('{}元素输入框操作清空'.format(locator))
        ele.clear()
    # 按回车--测试OK
    def enter(self, locator):
        ele = self.find_element(locator)
        logging.info('{}元素进行回车键操作'.format(logging))
        ele.send_keys(Keys.ENTER)
    # 全选  Ctrl+a--测试OK
    def ctrl_a(self,locator):
        ele = self.find_element(locator)
        logging.info('{}元素输入框内容进行全选操作'.format(locator))
        ele.send_keys(Keys.CONTROL, 'a')
    # 粘贴 Ctrl +x--测试OK
    def ctrl_x(self,locator):
        ele = self.find_element(locator)
        logging.info('{}元素输入框内容进行剪切操作'.format(locator))
        ele.send_keys(Keys.CONTROL, 'x')
    # 粘贴 Ctrl +v--测试OK
    def ctrl_v(self, locator):
        ele = self.find_element(locator)
        logging.info('{}元素输入框内容进行粘贴操作'.format(locator))
        ele.send_keys(Keys.CONTROL, 'v')


    # 17. 等待操作
    def implicitly_wait(self, seconds=30):
        self.driver.implicitly_wait(seconds)
    def wait(self, seconds=20):
        time.sleep(seconds)

    # 18.浏览器操作
    def open_url(self, url):
        self.driver.get(url)
        logging.info('打开URL地址%s;' % url)
    def set_browser_max(self):
        self.driver.maximize_window()
        logging.info("设置浏览器的最大化")
    def close_tab(self):
        self.driver.close()
        logging.info('关闭当前的tab页签')
    def set_browser_min(self):
        self.driver.minimize_window()
        logging.info("设置浏览器的最小化")
    def browser_refresh(self):
        self.driver.refresh()
        logging.info("浏览器的刷新操作")
    def get_title(self):
        value = self.driver.title
        logging.info("获取网页的标题为:%s" % value)
        return value
    def quit_browser(self):
        self.driver.quit()
        logging.info("关闭浏览器")

2.2 file_path.py 路径文件

# —— coding :utf-8 ——
# @time:    2020/10/7 20:58
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:   [email protected]
# @File:    file_path.py
import os

# base_path = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] 二选1
base_path = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0]

# 测试数据路径
TestDatas_path = os.path.join(base_path, 'TestDatas')

# 测试用例路径
TestCases_path = os.path.join(base_path, 'TestCases')

# 报告路径
reports_path = os.path.join(base_path, r'Outputs\reports')
allure_reports_path = os.path.join(base_path,r'Outputs\allure_reports')

# 截图路径
screenshots_path = os.path.join(base_path, r'Outputs\screenshots')

# 日志路径
logs_path = os.path.join(base_path, r'Outputs\logs')

# config_dir = os.path.join(base_path,'Config')

if __name__ == '__main__':
    print(logs_path)

2.3 my_log.py 自定义日志配置文件

import logging
from logging.handlers import RotatingFileHandler
import time
from Common import file_dir

fmt = " %(asctime)s  %(levelname)s %(filename)s %(funcName)s [ line:%(lineno)d ] %(message)s"
datefmt = '%a, %d %b %Y %H:%M:%S'

handler_1 = logging.StreamHandler()

curTime = time.strftime("%Y-%m-%d %H%M", time.localtime())

handler_2 = RotatingFileHandler(file_dir.logs_path + "/Web_Autotest_{0}.log".format(curTime), backupCount=20, encoding='utf-8')

#设置rootlogger 的输出内容形式,输出渠道
logging.basicConfig(format=fmt,datefmt=datefmt,level=logging.INFO,handlers=[handler_1,handler_2])

2.4 send_email.py 发送邮件文件

# —— coding :utf-8 ——
# @time:    2020/10/7 20:59
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:   [email protected]
# @File:    send_email.py
"""
使用一个邮箱向另一个邮箱发送测试报告的html文件,这里需要对发送邮件的邮箱进行设置,获取邮箱授权码。
username=“发送邮件的邮箱”, password=“邮箱授权码”
这里要特别注意password不是邮箱密码而是邮箱授权码。
mail_server = "发送邮箱的服务器地址"
这里常用的有 qq邮箱——"stmp.qq.com", 163邮箱——"stmp.163.com"
其他邮箱服务器地址可自行百度
"""
import os
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import time

# 自动发送邮件
class SendEmail():
    def send_email(self, new_report):
        # 读取测试报告中的内容作为邮件的内容
        with open(new_report, 'r', encoding='utf8') as f:
            mail_body = f.read()
        # 发件人地址
        send_addr = '[email protected]'
        # 收件人地址
        reciver_addr = '[email protected]'
        # 发送邮箱的服务器地址 qq邮箱是'smtp.qq.com', 163邮箱是'smtp.163.com'
        mail_server = 'smtp.qq.com'
        now = time.strftime("%Y-%m-%d %H:%M:%S")
        # 邮件标题
        subject = '接口自动化测试报告' + now
        # 发件人的邮箱及邮箱授权码
        username = '[email protected]'
        password = 'moasmyxgojcgbbch'  # 注意这里是邮箱的授权码而不是邮箱密码
        # 邮箱的内容和标题
        message = MIMEText(mail_body, 'html', 'utf8')   # MIMEText(邮箱主体内容,内容类型,字符集)
        message['Subject'] = Header(subject, charset='utf8')
        # 发送邮件,使用的使smtp协议
        smtp = smtplib.SMTP()
        smtp.connect(mail_server)
        smtp.login(username, password)
        smtp.sendmail(send_addr, reciver_addr.split(','), message.as_string())
        smtp.quit()

    # 获取最新的测试报告地址
    def acquire_report_address(self, reports_address):
        # 测试报告文件夹中的所有文件加入到列表
        test_reports_list = os.listdir(reports_address)
        # 按照升序排序生成新的列表
        new_test_reports_list = sorted(test_reports_list)
        # 获取最新的测试报告
        the_last_report = new_test_reports_list[-1]
        # 最新的测试报告地址
        the_last_report_address = os.path.join(reports_address, the_last_report)
        return the_last_report_address




if __name__ == '__main__':
    # 测试报告存放位置
    test_reports_address = '../test_result/html_report'
    # 查找最新生成的测试报告地址
    new_report_addr = SendEmail().acquire_report_address(test_reports_address)
    # 自动发送邮件
    SendEmail().send_email(new_report_addr)

3. 输出层

此前已经解析,不再重复

4. 页面元素定位层

4.1 indexpage_locators.py 首页元素定位文件

from selenium.webdriver.common.by import By
# 用户名
user_link = (By.XPATH,'//a[@href="/Member/index.html"]')
# 投标按钮
bid_button = (By.XPATH,'//*[text()="抢投标"]')    #//a[@class="btn btn-special"]

4.2 investpage_locators.py 投资页面元素定位文件

# —— coding :utf-8 ——
# @time:    2020/10/7 20:59
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:   [email protected]
# @File:    investpage_locators.py
from selenium.webdriver.common.by import By
# 投资输入框
money_input = (By.XPATH,'//input[contains(@class,"invest-unit-investinput")]')

#投标点击按钮
invest_button = (By.XPATH,'//div[@class="body"]//button')

# 投资成功弹窗的查看并激活按钮
active_button_on_success_pop = (By.XPATH,'//div[@class="layui-layer-content"]//button')

# 投标按钮上的错误信息
errorMsg_from_investButton = (By.XPATH,'//div[@class="layui-layer-content"]')

# 页面中间的错误提示信息
errorMsg_from_pageCenter = (By.XPATH,'//div/button[@class="btn btn-special height_style"]/text()')

4.3 loginpage_locators.py 登录页元素定位文件

4.4 其他页面元素定位根据需求自行创建封装

5. 页面对象层(操作方法)

5.1 index_page.py

# —— coding :utf-8 ——
# @time:    2020/10/7 21:00
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:  [email protected]
# @File:    index_page.py

import random
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from PageLocators import indexpage_locators as loc
class IndexPage:
    def __init__(self,driver):
        self.driver = driver
    def isExist_logout_ele(self):
        # 等待5s,元素没有出现 //a[@href="/Index/logout.html"]
        # 如果存在返回True,如果不存在,返回False
        try:
            WebDriverWait(self.driver, 5).until(EC.visibility_of_element_located((By.XPATH, '//a[@href="/Index/logout.html"]')))
            return True
        except:
            return False

    # 选标操作 --默认选第一个 = 元素定位的时候,过滤掉不可以投的标
    def click_first_bid(self):
        WebDriverWait(self.driver,10).until(EC.visibility_of_element_located(loc.bid_button))
        self.driver.find_element(*loc.bid_button).click()
    # 随机选一个标 //*[text()="抢投标"]
    def click_bid_by_random(self):
        # 找到所有符合的标
        WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(loc.bid_button))
        eles = self.driver.find_elements(*loc.bid_button)
        # 随机数
        index = random.randint(0,len(eles)-1)
        eles[index].click()

5.2 login_page.py

# —— coding :utf-8 ——
# @time:    2020/10/7 21:01
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:  [email protected]
# @File:    login_page.py
from PageLocators.loginpage_locators import LoginPageLocator as loc
from Common.base_page import BasePage

class LoginPage(BasePage):
    '''登录页面类'''
    # 登录操作
    def login(self, username, password, remember_user=True):
        # 输入用户名
        # 输入密码
        # 点击登录
        doc = "登录页面_登录功能"
        self.wait_eleVisible(loc.user_input,doc=doc)
        self.input_text(loc.user_input,username,doc)
        self.input_text(loc.pwd_input,password,doc)
        # 判断remember_user的值,决定是否勾选
        self.click_element(loc.login_but,doc)

    # 获取错误提示信息 ----登录区域
    def get_errorMsg_from_loginArea(self):
        doc = "登录页面_获取登录区域错误提示信息"
        self.wait_eleVisible(loc.get_errorMsg_from_login,doc=doc)
        return self.get_element_text(loc.get_errorMsg_from_login,doc)

    # 获取错误提示信息---中间区域
    def get_login_wrongPwd_noRegArea(self):
        doc = "登录页面_获取中间区域错误提示信息"
        self.wait_eleVisible(loc.get_login_wrongPwd_noReg,poll_frequency=0.2,doc=doc) # 错误的密码和没有注册提示信息在中间区域显示
        return self.get_element_text(loc.get_login_wrongPwd_noReg,doc)

5.3 其他页面元素对象操作方法根据需求自行创建函数封装

6. 测试用例层

6.1 conftest.py前后置操作文件,如打开浏览器、关闭浏览器、浏览器最大化、刷新页面等

# —— coding :utf-8 ——
# @time:    2020/10/7 21:01
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:  [email protected]
# @File:    conftest.py
from selenium import webdriver
from TestDatas import Common_datas as CD
from PageObjects.login_page import LoginPage
import pytest


driver = None
# 声明是一个fixture
@pytest.fixture(scope="class")
def access_web():
    global driver
    # 前置操作
    print("所有用例执行之前,setup整个测试类执行一次")
    driver = webdriver.Chrome()
    driver.get(CD.web_login_url)
    lg = LoginPage(driver)
    yield (driver, lg)  # 分割线:返回值
    # 后置操作
    print("所有用例执行完成,tearDown整个测试类执行一次")
    driver.quit()

@pytest.fixture
def refresh_page():
    global driver
    driver = webdriver.Chrome()
    # 前置操作
    yield
    # 后置操作
    driver.refresh()

6.2 test_login.py

# —— coding :utf-8 ——
# @time:    2020/10/7 21:02
# @IDE:     webTest_frmewok_v2.0
# @Author:  jsonLiu
# @Email:   [email protected]
# @File:    test_login.py
import unittest
from selenium import webdriver
from PageObjects.login_page import LoginPage
from PageObjects.index_page import IndexPage
import ddt
from TestDatas import Common_datas as CD
from TestDatas import login_datas as LD
import pytest


@pytest.mark.usefixtures("access_web")
@pytest.mark.usefixtures("refresh_page")
class TestLogin():
    # TestCase中不能写__init__
    # @classmethod
    # def setUpClass(cls):
    #     #通过excel读取本功能当中需要的所有测试数
    #     cls.driver = webdriver.Chrome()
    #     cls.driver.maximize_window()
    #     cls.driver.get(CD.web_login_url)
    #     cls.lg = LoginPage(cls.driver)
    #
    # @classmethod
    # def tearDownClass(cls):
    #     cls.driver.quit()
    # # def setUp(self):
    # #     pass
    # def tearDown(self):
    #     '''后置'''
    #     # 正常用例 -登录成功
    #     self.driver.refresh()

    # 异常用例 - 错误的手机号格式(大于11位、小于11位、为空、不在号码段) ddt
    @pytest.mark.parametrize("data", LD.phone_data)  # 参数化,把LD.phone_data的测试数据交给自定义参数名为data的参数
    def test_login_0_user_wrongFormat(self, data, access_web):
        # 步骤 输入用户名:xxx,密码:xxx,点击登录
        access_web[1].login(data["user"], data["password"])
        # 断言 登录页面 提示:请输入正确手机号
        # 对别文件内容与期望值是否相等
        assert (access_web[0].get_errorMsg_from_loginArea(), data["check"])

    # 手机号没有注册
    @pytest.mark.parametrize("caseData", LD.wrongPwd_noReg_data)
    def test_login_1_wrongPwd_noReg(self, caseData,access_web ):
        # 步骤:输入用户名:xxx, 密码:xxx 点击登录
        access_web["user"].login(caseData["user"],caseData["password"])
        # 断言: 登录页面 页面中间提示:此账号没有经过授权,请联系管理员! / 密码错误
        # 登录页面中, --获取提示框文本内容
        # 对比文本内容与期望值是否相等
        assert (access_web[0].get_errorMsg_from_loginArea(), caseData["check"])

    # fixture的函数名称用来接收它的返回值
    @pytest.mark.smoke
    def test_login_2_normal(self, access_web):
        # 步骤 输入用户名:xxx,密码:xxx,点击登录
        access_web[1].login(LD.success_data["user"], LD.success_data["password"])
        # 断言 首页当中能找到“我的帐户”或“退出"关键字
        assert IndexPage(access_web[0]).isExist_logout_ele()

if __name__ == '__main__':
    pass

7. 测试数据层

7.1 Common_datas.py

# 全局 --系统访问地址 --登录链接
web_login_url = "http://path_url/Index/login.html"

web_invest_url = "http://path_url/loan/loan_detail/Id/25750.html"

7.2 login_datas.py

# 正常用例 -登录成功
success_data = {"user":"13457681358","password":"123456"}

# 异常用例 - 错误的手机号格式(大于11位、小于11位、为空、不在号码段)
phone_data = [
    {"user":"1345768135","password":"123456","check":"请输入正确的手机号"},  # 小于11位
    {"user":"134576813588","password":"123456","check":"请输入正确的手机号"}, #大于11位
    {"user":"","password":"123456","check":"请输入手机号"},  # 手机号为空
    {"user":"13457681358","password":"123456","check":"请输入正确的手机号"} # 不在手机号段
]

wrongPwd_noReg_data = [
    {"user":"13457681358","password":"12345","check":"帐号或密码错误!"},  # 密码错误
    {"user":"13457681357","password":"123456","check":"此账号没有经过授权,请联系管理员!"}, # 账号没有授权
]

7.3 其他测试数据文件根据项目需要自行创建

8. readme.md 帮助文件

web自动化测试基本框架
-Common
-Outputs
-PageLocators
-PageObjects
-TestCases
-TestDatas
-run.py

常用命令:
创建虚拟环境		mkvirtualenv -p python3 虚拟环境名字
查看虚拟环境		workon
切换虚拟环境   	workon 虚拟环境名字`
退出当前虚拟环境	deactivate
删除虚拟环境		rmvirtualenv 虚拟环境名字`
导入requirements.txt工具包命令: pip install -r requirements.txt
导入requirements.txt工具包命令: pip freeze > requirements.txt
安装命令: pip install 包名
移除命令: pip uninstall 包名
查看已安装: pip list

9. requirements.txt 项目依赖工具包

atomicwrites==1.4.0
attrs==20.2.0
colorama==0.4.3
ddt==1.4.1
iniconfig==1.0.1
more-itertools==8.5.0
packaging==20.4
pluggy==0.13.1
py==1.9.0
pyparsing==2.4.7
pytest==6.0.2
pytest-html==2.1.1
pytest-metadata==1.10.0
pytest-rerunfailures==9.1
pywin32==228
selenium==3.141.0
six==1.15.0
toml==0.10.1
urllib3==1.25.10

10.run.py 项目运行文件

import HTMLTestRunner
from  Common.file_path import *
import unittest
# 实例化套件对象
s = unittest.TestSuite()
# 1.实例化TestLoader对象
# 2. 使用discover找到一个目录下所有测试用例
# 使用s
loader = unittest.TestLoader()
s.addTests(loader.discover(testcases_dir))
# # 运行
# runner = unittest.TextTestRunner()
# runner.run()

fp = open(htmlreport_dir+'/autoTest_report.html','wb')
runner = HTMLTestRunner.HTMLTestRunner(
        stream=fp,
        title="单元测试报告",
        description="单元测试报告"

)
runner.run(s)

四、与Jenkins集成

关于allure报告与Jenkins集成

  • windows版可参考此篇
    https://blog.csdn.net/weixin_43723664/article/details/105489504
  • linux版可参考此篇: https://www.cnblogs.com/wang-mengmeng/p/11784805.html

你可能感兴趣的:(#,python自动化,软件测试,selenium)