Selenium Web自动化测试框架实践

项目背景

https://passport.csdn.net/login CSDN登录页面

功能实现

  • 自动运行用例
  • 自动生成测试报告
  • 自动断言与截图
  • 自动将最新测试报告发送到指定邮箱
  • 数据,页面元素分离
  • PageObject+Unittest+ddt数据驱动用例
  • 执行日志、分布式执行

项目架构

 

 

 

浏览器driver定义

from common.readFile import ReadFile
from common.logger import Logger
from selenium import webdriver

logger = Logger()

from selenium.webdriver import Remote

class Browser():

    def __init__(self):
        config = ReadFile()
        self.browser = config.readConfig("Browser", "browser")
        self.host = config.readConfig("host","host")
        logger.info("You had select {} host {} browser.".format(self.host,self.browser))

    def driver(self):
        """
        启动浏览器驱动
        :return: 返回浏览器驱动URL
        """
        try:
            # driver = webdriver.Chrome()
            driver = Remote(command_executor='http://' + self.host + '/wd/hub',
                            desired_capabilities={ 'platform': 'ANY',
                                                   'browserName': self.browser,
                                                   'version': "",
                                                   'javascriptEnabled': True
                                                }
                            )
            return driver
        except Exception as msg:
            print("驱动异常-> {0}".format(msg))

用例运行前后的环境准备工作

import unittest
from common.driver import Browser

class StartEnd(unittest.TestCase):
    def setUp(self):
        self.driver = Browser().driver()
        self.driver.implicitly_wait(10)
        self.driver.maximize_window()

    def tearDown(self):
        self.driver.quit()

工具方法模块

主要封装一些公共的方法如:截图,查找最新报告

import time
from selenium import webdriver

import os,sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))

from config import setting

def inser_img(driver):
    # 指定截图存放的根目录路径
    screen_dir = setting.TEST_REPORT + '/imges/'
    rq = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
    screen_name = screen_dir + rq + '.png'
    driver.get_screenshot_as_file(screen_name)
    print('screenshot:' + screen_name)

#查找最新的测试报告
def latest_report(report_dir):
    lists = os.listdir(report_dir)
    lists.sort(key=lambda fn: os.path.getatime(report_dir + '\\' + fn))
    file = os.path.join(report_dir, lists[-1])
    return file

def latest_report_img(report_dir):
    lists = os.listdir(report_dir)
    lists.sort(key=lambda fn: os.path.getatime(report_dir + '\\' + fn))
    file = os.path.join(report_dir, lists[-1])
    return file

Pageobject页面对象封装

基础页面类

import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from common.logger import Logger
from common.readFile import ReadFile

logger = Logger()

class BasePage():
    "定义一个页面基类,让所有页面都继承这个类,封装一些常用的页面操作方法到这个类"

    def __init__(self, driver):
        self.driver = driver
        config = ReadFile()
        self.baseurl = config.readConfig("BaseUrl", "url")

    def open_url(self, url):
        self.driver.get(self.baseurl + url)

    # 退出浏览器
    def quit_browser(self):
        self.driver.quit()

    # 浏览器前进操作
    def forward(self):
        self.driver.forward()

    # 浏览器后退操作
    def back(self):
        self.driver.back()

    # 隐式等待
    def wait(self, seconds):
        self.driver.implicitly_wait(seconds)

    # 查找元素
    def find_element(self, selector):
        selector_by = selector['find_type']
        selector_value = selector['element_info']

        try:
            if selector_by == 'id':
                el = self.driver.find_element_by_id(selector_value)
            elif selector_by == "n" or selector_by == 'name':
                el = self.driver.find_element_by_name(selector_value)
            elif selector_by == 'cs' or selector_by == 'css_selector':
                el = self.driver.find_element_by_css_selector(selector_value)
            elif selector_by == 'cn' or selector_by == 'classname':
                el = self.driver.find_element_by_class_name(selector_value)
            elif selector_by == "lt" or selector_by == 'link_text':
                el = self.driver.find_element_by_link_text(selector_value)
            elif selector_by == "plt" or selector_by == 'partial_link_text':
                el = self.driver.find_element_by_partial_link_text(selector_value)
            elif selector_by == "tn" or selector_by == 'tag_name':
                el = self.driver.find_element_by_tag_name(selector_value)
            elif selector_by == "x" or selector_by == 'xpath':
                el = self.driver.find_element_by_xpath(selector_value)
            elif selector_by == "ss" or selector_by == 'selector_selector':
                el = self.driver.find_element_by_css_selector(selector_value)
            else:
                raise NameError("Please enter a valid type of targeting elements.")
        except NoSuchElementException  :
            logger.error("{0}页面中未能找到{1}元素".format(self, selector_value))

        return el

    # 输入
    def input(self, selector, text):
        el = self.find_element(selector)
        try:
            el.clear()
            el.send_keys(text)
            logger.info("Had type \' %s \' in inputBox" % text)
        except NameError as e:
            logger.error("Failed to type in input box with %s" % e)

    # 点击
    def click(self, selector):
        el = self.find_element(selector)
        try:
            logger.info("The element \' %s \' was clicked." % el.text)
            el.click()
        except NameError as e:
            logger.error("Failed to click the element with %s" % e)

    @staticmethod
    def sleep(seconds):
        time.sleep(seconds)
        logger.info("Sleep for %d seconds" % seconds)

    def get_text(self,selector):
        el = self.find_element(selector)
        try:
            return el.text
        except NameError as e:
            logger.error("Failed to text the element with %s" % e)

    def switch_frame(self, selector):
        """
        多表单嵌套切换
        :param loc: 传元素的属性值
        :return: 定位到的元素
        """
        try:
            el = self.find_element(selector)
            return self.driver.switch_to_frame(el)
        except NoSuchElementException as e:
            logger.error("查找iframe异常-> {0}".format(e))

    def switch_windows(self, selector):
        """
        多窗口切换
        :param loc:
        :return:
        """
        try:
            el = self.find_element(selector)
            return self.driver.switch_to_window(el)
        except NoSuchElementException as e:
            logger.error("查找窗口句柄handle异常-> {0}".format(e))

    def switch_alert(self):
        """
        警告框处理
        :return:
        """
        try:
            return self.driver.switch_to_alert()
        except NoSuchElementException as e:
            logger.error("查找alert弹出框异常-> {0}".format(e))

LoginPage.py —— CNDS登录页面

from  pageObject.basePage import *
from selenium import webdriver
from common.readFile import ReadFile
from config import setting

login_el = ReadFile().readYaml(setting.TEST_Element_YAML + '/' + 'login.yaml')
data = ReadFile().readYaml(setting.TEST_DATA_YAML + '/' + 'login_data.yaml')

class CndsPage(BasePage):
    '''登录页面'''

    url = '/login'

    # 定位器,通过元素属性定位元素对象
    #选择账号密码登录
    chanlelogin_loc = login_el['testcase'][0]
    # 账号输入框
    username_loc = login_el['testcase'][1]
    # 密码输入框
    pwd_loc = login_el['testcase'][2]
    # 单击登录
    login_accout_loc = login_el['testcase'][3]

    def accout_login(self,accout,passwd):
        self.open_url(self.url)
        self.click(self.chanlelogin_loc)
        self.input(self.username_loc,accout)
        self.input(self.pwd_loc,passwd)
        self.click(self.login_accout_loc)

    # 定位器,通过元素属性定位检查项元素对象
    user_login_success_loc = login_el['check'][0]
    accout_id_loc = login_el['check'][1]
    accout_pawd_error_loc = login_el['check'][2]

    # 账号或密码错误提示
    def accout_passwd_error(self):
        return self.get_text(self.accout_pawd_error_loc)

    # 登录成功,跳转到个人资料页,获取用户名
    def get_account(self):
        self.click(self.user_login_success_loc)
        time.sleep(2)

    def user_login_success(self):
        return self.find_element(self.accout_id_loc).text

组织测试用例

  • 用户名密码正确点击登录
  • 用户名正确,密码错误点击登录
import unittest
from common import function,myUnit,readFile
from pageObject.loginPage import CndsPage
from time import sleep
from common.logger import Logger
from config import setting
import ddt

log = Logger()

testData= readFile.ReadFile().readYaml(setting.TEST_DATA_YAML + '/' + 'login_data.yaml')

@ddt.ddt
class LoginTest(myUnit.StartEnd):
    # @unittest.skip('skip this case')
    """CNDS登录测试"""
    def user_login_verify(self,account,passwd):
        """
        用户登录
        :param :account 账号
        :param passwd: 密码
        :return:
        """
        CndsPage(self.driver).accout_login(account,passwd)

    @ddt.data(*testData)
    def test_login_normal(self,datayaml):
        log.info("test_login1_normal is start run...")
        self.user_login_verify(datayaml['data']['accout'],datayaml['data']['passwd'])
        sleep(3)
        #断言与截屏
        po = CndsPage(self.driver)
        if datayaml['screenshot'] == 'login_success':
            po.get_account()
            function.inser_img(self.driver)
            self.assertEqual(po.user_login_success(), datayaml['check'][0], "登录成功,返回实际结果是->: {0}".format(po.user_login_success()))
        else:
            function.inser_img(self.driver)
            self.assertEqual(po.accout_passwd_error(), datayaml['check'][0],"登录失败,返回实际结果是->: {0}".format(po.accout_passwd_error()))
        print("test_login1_normal is test end!")

 执行测试用例

import unittest
from  common.function import latest_report
from  common.sendMail import *
from config import setting
from thridLib.HTMLTestRunner import HTMLTestRunner
import time
import os,sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
report_dir = setting.TEST_REPORT + '/report/'

def add_case(test_path=setting.TEST_DIR):
    discover = unittest.defaultTestLoader.discover(test_path, pattern="test*.py")
    return discover

def run_case(all_case,result_path=report_dir):
    print("start run testcase...")
    now = time.strftime("%Y-%m-%d %H_%M_%S")
    report_name = result_path + '/' + now + 'result.html'
    print("start write report...")

    #HTMLTestRunner测试报告
    with open(report_name, 'wb') as f:
        runner = HTMLTestRunner(stream=f, title='测试报告', description='用例执行情况')  # 定义测试报告
        runner.run(all_case)  # 执行测试用例
    f.close()

    print("find latest report...")
    # 查找最新的测试报告
    report = latest_report(result_path)
    # 邮件发送报告
    print("send email report...")
    send_mail(report)
    print("test end!")

if __name__ == '__main__':
    cases = add_case()
    run_case(cases)

 

 

 

 

详细可到git地址:https://github.com/wateryhh/webAutoTest

 

posted @ 2020-11-04 22:28  Water_you  阅读(292)  评论(0编辑  收藏  举报