首页 > 编程语言 >Python+selenium+unittest框架实现网易邮箱的自动登录

Python+selenium+unittest框架实现网易邮箱的自动登录

时间:2024-06-21 19:00:38浏览次数:14  
标签:__ get Python driver unittest selenium path self def

文章目录

概要

  本实例只针对简单的账密登录场景做处理,涉及登录方式切换、人机检测部分未作处理,后续会跟进处理。

整体架构流程

  • 依赖环境

        Python3.7

        selenium 3.141.0

        ddt 1.6.0

        

  • 目录结构

    

  目录说明

1. base: 公共基础方法,定位元素
2. business: 业务层,处理具体一件事情,如登录
3. case: 用例
4. config: 全局配置文件,主要配置项目 url 及页面元素定位信息
5. handle: 处理具体一件事情的某个步骤,比如输入用户名,输入验证码等
6. log: 日志文件夹
7. page: 定位具体页面的元素
8. report: 报告路径
9. screenCapture: 屏幕截图
10. util: 工具类方法,比如读取配置,获取验证码等
  • 调用关系
case->business->handle->page
  • 代码构建

1、创建projectConfig.ini项目配置文件

test/config/projectConfig.ini

[Project]
loginUrl = https://mail.163.com
timeout=5
username = username
password = password

2、创建globalElConfig.ini全局元素配置文件

test/config/globalElConfig.ini

[Login]
Tips=xpath:/html/body/header/div[1]/ul[1]/li[1]/div/span[1]
search_iframe=xpath://iframe[starts-with(@id,"x-URS")]
search_iframe2=xpath://iframe[starts-with(@id,"getMarkedContacts")]
option=xpath:lbApp
username=xpath://input[@name="email"]
password=xpath://input[@name="password"]
login_btn=xpath://*[@id="dologin"]

3、创建配置读取公共类

test/config/read_ini.py

# coding=utf-8

import configparser



class ReadIni(object):

    def __init__(self, node, file_name, encoding='utf-8'):
        self.node = node
        self.encoding = encoding
        self.cf = self.load_ini(file_name)

    def load_ini(self, file_name):
        cf = configparser.ConfigParser()
        cf.read(file_name, encoding=self.encoding)
        return cf

    def get_value(self, key):
        return self.cf.get(self.node, key)



4、创建Log日志类,记录执行日志、错误日志

test/log/user_log.py

import logging
import os
import datetime


class UserLog():
    def __init__(self):
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.INFO)
        self.fileStream = logging.FileHandler(self.__get_log_name(),encoding='utf-8')
        self.__init_logger_handle()

    def __get_log_name(self):
        log_path = os.path.join(os.path.dirname(
            os.path.abspath(__file__)), "logs")
        log_file = datetime.datetime.now().strftime("%Y-%m-%d")+".log"
        return log_path+"/"+log_file

    def __init_logger_handle(self):
        formatter = logging.Formatter(
            '%(asctime)s %(filename)s %(funcName)s %(lineno)s %(levelname)s --->%(message)s')
        self.fileStream.setFormatter(formatter)
        self.logger.addHandler(self.fileStream)

    def close(self):
        self.fileStream.close()
        self.logger.removeHandler(self.fileStream)

    def get_logger(self):
        return self.logger


if __name__ == "__main__":
    log = UserLog()
    logger = log.get_logger()
    logger.info("打一年工搬一年砖")
    log.close()

5、创建元素查找公共类

test/base/find_element.py

# encoding=utf-8
from config.read_ini import ReadIni
import os
from selenium.webdriver.common.by import By
import selenium.webdriver.support.expected_conditions as EC
import selenium.webdriver.support.ui as ui


class FindElement(object):

    def __init__(self, driver, node,
                 config=os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config',
                                     'globalElConfig.ini')):
        self.driver = driver
        self.read_ini = ReadIni(node, config)
        self.read_project = ReadIni('Project',
                                    os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config',
                                                 'projectConfig.ini'))
        self.config = config

    def get_element(self, key):
        timeout = self.read_project.get_value('timeout')
        webDriverWait = ui.WebDriverWait(self.driver, int(timeout))
        try:
            by, value = self.get_locator(key)
            if not by or not value:
                print(f"Failed to get locator for key {key}")
                return None
            print(f"Finding element by {by} with value {value}")
            if by == 'id':
                return webDriverWait.until(EC.presence_of_element_located((By.ID, value)))
            elif by == 'name':
                return webDriverWait.until(EC.presence_of_element_located((By.NAME, value)))
            elif by == 'classname':
                return webDriverWait.until(EC.presence_of_element_located((By.CLASS_NAME, value)))
            elif by == 'cssSelector':
                return webDriverWait.until(EC.presence_of_element_located((By.CSS_SELECTOR, value)))
            else:
                return webDriverWait.until(EC.presence_of_element_located((By.XPATH, value)))
        except Exception as e:
            print(f"Exception occurred while finding element: {e}")
            return None

    def get_elements(self, key):
        timeout = self.read_project.get_value('timeout')
        webDriverWait = ui.WebDriverWait(self.driver, int(timeout))
        try:
            by, value = self.get_locator(key)
            if not by or not value:
                print(f"Failed to get locator for key {key}")
                return None
            print(f"Finding elements by {by} with value {value}")
            if by == 'id':
                return webDriverWait.until(EC.visibility_of_all_elements_located((By.ID, value)))
            elif by == 'name':
                return webDriverWait.until(EC.visibility_of_all_elements_located((By.NAME, value)))
            elif by == 'classname':
                return webDriverWait.until(EC.visibility_of_all_elements_located((By.CLASS_NAME, value)))
            elif by == 'cssSelector':
                return webDriverWait.until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, value)))
            else:
                return webDriverWait.until(EC.visibility_of_all_elements_located((By.XPATH, value)))
        except Exception as e:
            print(f"Exception occurred while finding elements: {e}")
            return None

6、创建webdriver工具类

test/util/webdriver.py

import json
import sys

from selenium import webdriver
from selenium.webdriver import DesiredCapabilities


class myWebdriver(object):
    @staticmethod
    def get_driver():
        chrome_options = webdriver.ChromeOptions()  # 创建 Chrome 浏览器选项
        chrome_options.add_experimental_option('w3c', False)  # 设置 w3c 实验性选项为 False,确保浏览器兼容
        caps = DesiredCapabilities.CHROME  # 设置浏览器能力 (caps):
        caps['loggingPrefs'] = {'performance': 'ALL'}  # 设置了 loggingPrefs,以便收集所有性能相关的日志
        chrome_options.add_argument('--disable-gpu')  # 添加禁用 GPU 的选项
        if 'linux' in sys.platform:  # 根据操作系统设置无头模式
            chrome_options.add_argument('--headless')
        driver = webdriver.Chrome(desired_capabilities=caps, options=chrome_options)  # 创建 Chrome 浏览器驱动
        driver.implicitly_wait(10)  # 设置隐式等待时间:10s
        driver.set_window_size(1936,
                               1056) if 'linux' in sys.platform else driver.maximize_window()  # 最大化窗口在--headless模式下无效,导致linux截图太小,所以手动设置
        return driver

    @staticmethod
    def save_performance_log(driver, filename):
        logs = [json.loads(log['message'])['message'] for log in driver.get_log("performance")]
        with open(filename, 'a') as f:
            json.dump(logs, f, indent=4, ensure_ascii=False)

7、创建页面page元素获取类,处理具体一件事情的某个步骤

test/page/login_page.py

# coding=utf-8
from base.find_element import FindElement
import os


class LoginPage(object):
    #  获取globalElConfig.ini配置Login节点下配置
    def __init__(self, driver):
        self.fd = FindElement(driver, 'Login')

    # 初始化iframe框架,默认嵌套一层
    def get_iframe_element(self):
        return self.fd.get_element("search_iframe")

    #  多个iframe框架切换
    def get_iframe2_element(self):
        return self.fd.get_element("search_iframe2")

    #  获取断言结果元素
    def get_err_tips_elements(self):
        return self.fd.get_elements("errTips")

    #  获取账密登录按钮
    def get_option_element(self):
        return self.fd.get_element("option")

    # 获取用户名元素
    def get_username_element(self):
        return self.fd.get_element("username")

    # 获取密码元素
    def get_password_element(self):
        return self.fd.get_element("password")

    # 获取登录按钮元素
    def get_login_btn_element(self):
        return self.fd.get_element("login_btn")

9、创建handle应用类,处理

test/handle/login_handle.py

from handle.handle import Handle
from page.login_page import LoginPage


class LoginHandle(Handle):
    def __init__(self, driver):
        super().__init__(driver)
        self.login_p = LoginPage(driver)

    # iframe框架调用
    def into_iframe_work(self):
        frame = self.login_p.get_iframe_element()
        self.driver.switch_to.frame(frame)

    def quit_iframe_work(self):
        self.driver.switch_to.default_content()

    # 输入用户名
    def send_user_name(self, username):
        username_element = self.login_p.get_username_element()
        username_element.send_keys(username)

    # 输入密码
    def send_user_password(self, password):
        password_element = self.login_p.get_password_element()
        password_element.send_keys(password)

    # 点击登录按钮
    def click_login_btn(self):
        self.login_p.get_login_btn_element().click()

    #  截取断言文本
    def get_tips(self):
        tips = self.login_p.get_err_tips_elements()
        if tips is None:
            print('元素未找到!')
            return None
        if len(tips) > 1:
            print('出现多条提示!')
        tip = tips[-1].text
        print(tip)
        return tip

10、创建business业务类,处理具体一件事情

test/business/login_business.py

# coding=utf-8
from handle.login_handle import LoginHandle


class LoginBusiness(object):
    def __init__(self, driver):
        self.login_h = LoginHandle(driver)

    def click_option_success(self, username, password):
        self.login_h.into_iframe_work()  # 进入iframe框架
        self.login_h.send_user_name(username)  # 输入用户名
        self.login_h.send_user_password(password)  # 输入密码
        self.login_h.click_login_btn()  # 点击登录按钮
        self.login_h.quit_iframe_work()  # 退出iframe框架,因为断言元素不在框架内
        return self.login_h.get_tips()  # 获取断言结果返回

11、创建TestCase类,初始化浏览器,利用ddt构建测试数据集,Unittest框架执行单元测试用例

test/case/login_case.py

# coding=utf-8
import os
import unittest
import datetime
import ddt
from log.user_log import UserLog
from business.login_business import LoginBusiness
from config.read_ini import ReadIni
from util.webdriver import myWebdriver


@ddt.ddt
class LoginCass(unittest.TestCase):
    @classmethod
    def setUpClass(cls):  # 读取配置节点
        cls.driver = myWebdriver.get_driver()
        cls.log = UserLog()
        cls.logger = cls.log.get_logger()
        cls.readIni = ReadIni('Project',
                              os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config',
                                           'projectConfig.ini'))
        cls.loginUrl = cls.readIni.get_value('loginUrl')

    def setUp(self):
        self.driver.get(self.loginUrl)
        self.login_b = LoginBusiness(self.driver)

    def tearDown(self):
        for method_name, error in self._outcome.errors:
            if error:
                # case_name=self._testMethodName
                self.logger.info(error)
                case_name = str(method_name)[:str(method_name).find("(")]
                file_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'screenCapture',
                                         case_name + '.png')
                self.driver.save_screenshot(file_path)
        log_file = 'login_case_' + datetime.datetime.now().strftime("%Y-%m-%d") + ".log"
        myWebdriver.save_performance_log(self.driver,
                                         os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
                                                      'log', 'performancelog', log_file))

    @classmethod
    def tearDownClass(cls):
        cls.driver.close()
        cls.log.close()
 #  通过[email protected]用户名断言判断是否登录成功
    @ddt.data(['username', 'password', '[email protected]'])
    @ddt.unpack
    def test_1_login_case(self, username, password, tips):
        actual_tips = self.login_b.click_option_success(username, password)
        self.assertEqual(actual_tips, tips)

12、创建HTMLTestRunner测试报告类,记录测试过程,保存测试结果test/util/HTMLTestRunner.py

HTMLTestRunner最新内容见:

http://tungwaiyip.info/software/HTMLTestRunner.html

13、创建Test_main测试套件,用于批量执行测试用例,生成测试报告

test/case/test_main.py

import os
import unittest
from util.HTMLTestRunner import HTMLTestRunner

if __name__ == "__main__":
    # unittest.main()
    report_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'report', 'result.html')
    with open(report_path, 'wb') as f:
        discover = unittest.defaultTestLoader.discover('./', pattern="*_case.py")  # 批量执行case用例
        #suite = unittest.TestLoader().loadTestsFromTestCase(LoginCass)
        runner = HTMLTestRunner(
            stream=f, title="web自动化测试报告", description=u"网易邮箱", verbosity=2)
        runner.run(discover)

执行Test_main查看Result结果:

技术细节

  • driver.switch_to.frame()  #iframe框架切换

 driver.switch_to.frame() 方法是 Selenium 中用于切换到特定 iframe 或 frame 的方法。在网页自动化测试中,很多网页可能包含 iframe(内嵌框架),这些框架是嵌套在主页面中的独立 HTML 文档。要在这些 iframe 中进行操作,需要先切换到对应的 iframe。

语法:

//进入iframe框架     driver.switch_to.frame(reference)

//退出iframe框架     driver.switch_to.default_content()

参数:

reference 可以是以下三种之一:

  1. 索引(index):从 0 开始的整数,表示要切换到的 iframe 在页面上的顺序。
  2. 名字或 ID(name or id):iframe 的 nameid 属性的值。
  3. WebElement 对象:已经找到的 iframe 元素对象。

  • 分割定位器字符串,并提取定位器类型和值
# 分割定位器字符串
arr = value.split(':', 1)  # 只分割一次
if len(arr) != 2:
    print(f"Invalid locator format for key {key}: {value}")
    return None, None

  • 将读取到的值按照冒号 : 分割成两个部分,split(':', 1) 表示只分割一次。
  • 如果分割后的结果不是两个部分,则说明格式不正确,打印提示信息并返回 None, None
# 提取定位器类型和值
by, locator_value = arr[0], arr[1]
return by, locator_value
  • 将分割后的第一个部分赋值给 by,表示定位器类型(如 id, name, xpath 等)。
  • 将分割后的第二个部分赋值给 locator_value,表示定位器值。
  • 返回定位器类型和定位器值。

小结

       

UnitTest 是 Python 中的一个单元测试框架,用于编写和运行测试用例。它提供了一个结构化的方法来组织测试代码,并能够自动化执行测试并生成结果报告。

主要特点和用法:

  1. 测试用例(Test Case)

    • 测试用例是 unittest 的核心概念,通常继承自 unittest.TestCase 类。
    • 每个测试用例是一个独立的测试单元,用于验证被测试代码的某个方面或功能是否正确。
  2. 断言方法(Assertions)

    • unittest 提供了多种断言方法,用于检查预期结果与实际结果是否一致,例如 assertEqual(), assertTrue(), assertIn() 等。
    • 当断言失败时,会抛出 AssertionError 异常,标记测试失败。
  3. 测试装置(Test Fixture)

    • unittest 支持设置测试装置,包括 setUp()tearDown() 方法。
    • setUp() 方法在每个测试方法运行前执行,用于准备测试环境。
    • tearDown() 方法在每个测试方法运行后执行,用于清理测试环境。
  4. 测试套件(Test Suite)

    • 测试套件是一组相关的测试用例的集合,可以用来组织和运行多个测试。
    • 可以使用 unittest.TestSuite() 创建测试套件,并将测试用例添加到套件中。
  5. 运行器(Test Runner)

    • unittest 提供了命令行接口和 GUI 工具来运行测试用例。
    • 常用的运行器包括 unittest.TextTestRunner()(文本输出)、unittest.TextTestResult(详细文本输出)、unittest.XMLTestRunner(XML 格式输出)等。
  6. 跳过测试和预期异常

    • 可以使用装饰器 @unittest.skip(reason) 跳过某个测试用例。
    • 使用 @unittest.expectedFailure 装饰器标记预期会失败的测试用例。

标签:__,get,Python,driver,unittest,selenium,path,self,def
From: https://blog.csdn.net/backtwo/article/details/139830954

相关文章

  • Python标注工具labelImg使用Pyinstaller打包成EXE的过程及问题处理
    直接上过程1.在python项目中使用pip命令安装pyinstaller。2.在python编辑器(如Pycharm)终端切换到要打包的.py文件所在目录。3.使用pyinstaller工具命令打包.py文件,如:pyinstallerlabelImg.py--noconsole--workpath.\Pyinstaller\temp --distpath.\Pyinstaller\dist 4.......
  • python pyinstaller打包的exe 反编译问题记录 破解加密
    首先是用pyinstxtractor这个网上很多教程,不详说了。生成一个xxx.exe_extracted目录生成过程中,如果pyinstaller用key加密了,会[!]Error:FailedtodecompressPYZ-00.pyz_extracted\Cython\__init__.pyc,probablyencrypted.Extractingasis. 这个说是fail了,其实可以解......
  • 详解pip换源步骤,打造极速Python开发环境
    在当今日益数字化的世界中,Python及其包管理工具pip已成为开发者们不可缺少的工具。Python的广泛应用,从数据分析到人工智能,从Web开发到科学计算,都离不开大量高质量的库和包的支持。但是,在安装和管理这些库和包时,网络速度和源的可靠性往往成为制约效率的瓶颈。为了解决这一问题,......
  • Python批量保存Excel文件中的图表为图片
    Excel工作簿作为一款功能强大的数据处理与分析工具,被广泛应用于各种领域,不仅能够方便地组织和计算数据,还支持用户创建丰富多彩的图表,直观展示数据背后的洞察与趋势。然而,在报告编制、网页内容制作或分享数据分析成果时,直接嵌入整个Excel文件往往不够便捷,且可能受限于接收者......
  • vscode+robotframework的实践-selenium(更新中)
    一说明上一次使用robotframework还是2019年毕业刚进入工作的时候,使用的是robotframework的官方配套编辑器RIDE进行自动化脚本编写,在使用的过程中偶尔会遭遇卡顿、闪退等问题,当时排查问题大多数是因为RIDE自身与python版本以及操作系统之间的兼容性问题导致的,那时候没有编程意识......
  • python中的yield与yield from
    生成器与迭代器在Python中,迭代器就是可以用来迭代(比如for循环中的迭代)操作的对象,任何实现了__next__方法的对象都可以称之为迭代器。classFib:def__init__(self,n)->None:self.prev=0self.cur=1self.n=ndef__next__(self......
  • python rce
    之前学习过了rce在php下的利用,接下来来学习一下python中rce的利用,其根本主要就是执行系统命令的函数有所不同.os模块os是python中用来执行系统命令的包.下面是常用的两个方法.1.os.system:可以用来执行系统命令,但是无法将系统命令执行的结果返回.如果执行成功了会返回0,失败......
  • python读取excel文件
    在Python中,可以使用pandas库来读取Excel文件。首先,确保安装了pandas和openpyxl(用于处理Excel文件的库):pipinstallpandasopenpyxl以下是使用pandas读取Excel文件的示例代码:importpandasaspd#读取Excel文件df=pd.read_excel('example.xlsx')#显示数据框内容p......
  • Python学习之爬虫简单例子
    importBeautifulSoupimportpandasaspdimporturllib.request,urllib.errordefrequestUrl(url):  headers={    'User-Agent':"Mozilla/5.0(Macintosh;IntelMacOSX10_14_6)AppleWebKit/537.36(KHTML,likeGecko)Chrome/81.0.404......
  • 【python数据可视化】利用Python爬取天气数据并实现数据可视化,绘制天气轮播图
    用Python爬虫抓取全年天气数据并绘制天气轮播图一、运行结果:二、代码展示:由csv文件生成↓接下来是绘制天气轮播图运行结果:完整代码请看这里↓......