目录:导读
- 前言
- 一、Python编程入门到精通
- 二、接口自动化项目实战
- 三、Web自动化项目实战
- 四、App自动化项目实战
- 五、一线大厂简历
- 六、测试开发DevOps体系
- 七、常用自动化测试工具
- 八、JMeter性能测试
- 九、总结(尾部小惊喜)
前言
线性脚本
import logging
from appium import webdriver
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait
from appium.webdriver.common.mobileby import MobileBy as Bylogging.basicConfig(filename='./testLog.log', level=logging.INFO,format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')def android_driver():desired_caps = {"platformName": "Android","platformVersion": "10","deviceName": "PCT_AL10","appPackage": "com.ss.android.article.news","appActivity": ".activity.MainActivity","unicodeKeyboard": True,"resetKeyboard": True,"noReset": True,}logging.info("启动今日头条APP...")driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)return driverdef is_toast_exist(driver, text, timeout=20, poll_frequency=0.1):'''判断toast是否存在,是则返回True,否则返回False'''try:toast_loc = (By.XPATH, ".//*[contains(@text, %s)]" % text)WebDriverWait(driver, timeout, poll_frequency).until(ec.presence_of_element_located(toast_loc))return Trueexcept:return Falsedef login_test(driver):'''登录今日头条操作'''logging.info("开始登陆今日头条APP...")try:driver.find_element_by_id("com.ss.android.article.news:id/bu").send_keys("xxxxxxxx") # 输入账号driver.find_element_by_id("com.ss.android.article.news:id/c5").send_keys("xxxxxxxx") # 输入密码driver.find_element_by_id("com.ss.android.article.news:id/a2o").click() # 点击登录except Exception as e:logging.error("登录错误,原因为:{}".format(e))# 断言是否登录成功toast_el = is_toast_exist(driver, "登录成功")assert toast_el, Truelogging.info("登陆成功...")if __name__ == '__main__':driver = android_driver()login_test(driver)
但是,这种线性模式存在以下等缺点:
元素定位属性和代码混杂在一起,不方便后续维护;
公共模块和业务模块混合在一起,显得代码冗余;
适用测试场景太单一;
在业务场景较为简单时这样写似乎没问题,但一旦遇到产品需求变更、业务逻辑比较复杂,需要维护的时就会非常麻烦。
优化思路
将公共方法(如:is_toast_exist(),日志记录器等)抽离出来,放入单独模块;
将元素定位方法、元素属性值、测试业务代码分离;
登录操作单独封装成一个模块;
使用Unittest单元测试框架管理并执行测试用例;
基于以上思路,我们就需要引入Page Object测试设计模式。
Page Object 设计模式
Page Object模式是Selenium中的一种测试设计模式,是Selenium、appium自动化测试项目的最佳设计模式之一。Page Object的通常的做法是,将公共方法、逻辑操作(元素定位、操作步骤)、测试用例、测试数据和测试驱动相互分离。
可以理解为将测试项目进行如下分层:
公共方法层
逻辑操作层(元素定位,测试步骤)
测试用例层(测试业务)
测试数据层
测试驱动层(执行测试用例)
公共方法层,包括公共方法或基础方法。
逻辑操作层,主要是将每一个页面或该页面需要测试的某个功能涉及到的元素设计为一个class。
测试用例层,只需调用逻辑操作层中对应页面的class即可。
测试数据层,即测试数据分离,包括配置数据和测试数据,如Capabilities、登录账号密码。
测试驱动层,执行整个测试并生成测试报告。
Page Object 模式测试框架封装
1、公共方法层
封装App启动的Capabilities配置信息,baseDriver.py
import yaml
from appium import webdriver
from common.baseLog import loggerdef android_driver():stream = open("../config/desired_caps", "r")data = yaml.load(stream, Loader=yaml.FullLoader)desired_caps = {}desired_caps["platformName"] = data["Android"],desired_caps["platformVersion"] = data["platformVersion"],desired_caps["deviceName"] = data["deviceName"],desired_caps["appPackage"] = data["appPackage"],desired_caps["appActivity"] = data["appActivity"],desired_caps["unicodeKeyboard"] = data["unicodeKeyboard"],desired_caps["resetKeyboard"] = data["resetKeyboard"],desired_caps["noReset"] = data["noReset"],desired_caps["automationName"] = data["automationName"]# 启动apptry:driver = webdriver.Remote('http://' + str(data['ip']) + ':' + str(data['port']) + '/wd/hub', desired_caps)logger.info("APP启动成功...")driver.implicitly_wait(8)return driverexcept Exception as e:logger.error("APP启动失败,原因是:{}".format(e))if __name__ == '__main__':android_driver()
封装基础类,basePage.py
from common.baseLog import logger
from selenium.webdriver.support.ui import WebDriverWait
from appium.webdriver.common.mobileby import MobileBy as By
from selenium.webdriver.support import expected_conditions as ECclass BasePage:def __init__(self, driver):self.driver = driverdef get_visible_element(self, locator, timeout=20):'''获取可视元素'''try:return WebDriverWait(self.driver, timeout).until(EC.visibility_of_element_located(locator))except Exception as e:logger.error("获取元素失败:{}".format(e))def is_toast_exist(driver, text, timeout=20, poll_frequency=0.1):'''判断toast是否存在,是则返回True,否则返回False'''try:toast_loc = (By.XPATH, ".//*[contains(@text, %s)]" % text)WebDriverWait(driver, timeout, poll_frequency).until(EC.presence_of_element_located(toast_loc))return Trueexcept:return False
2、逻辑操作层
封装登录,login_page.py
from common.baseLog import logger
from common.basePage import BasePage
from appium.webdriver.common.mobileby import MobileBy as Byclass LoginPage(BasePage):username_inputBox = (By.ID, "com.ss.android.article.news:id/bu") # 登录页用户名输入框password_inputBox = (By.ID, "com.ss.android.article.news:id/c5") # 登录页密码输入框loginBtn = (By.ID, "com.ss.android.article.news:id/a2o") # 登录页登录按钮def login_action(self, username, password):logger.info("开始登录...")logger.info("输入用户名:{}".format(username))self.get_visible_element(self.username_inputBox).send_keys(username)logger.info("输入密码:{}".format(password))self.get_visible_element(self.password_inputBox).send_keys(password)self.get_visible_element(self.loginBtn).click()
3、测试用例层
封装setUp、tearDown,baseTest.py
import time
import unittest
from common.baseDriver import android_driverclass StartEnd(unittest.TestCase):def setUp(self) -> None:self.driver = android_driver()def tearDown(self) -> None:time.sleep(2)self.driver.close_app()
封装测试用例,test_login.py
from common.baseLog import logger
from common.baseTest import StartEnd
from page.login_page import LoginPageclass LoginTest(StartEnd):def test_login_right(self):logger.info("正确的账号、密码登录")l = LoginPage(self.driver)l.login_action("13838380000", "123456")result = l.is_toast_exist("登录成功")self.assertTrue(result)def test_login_error(self):logger.info("正确的账号、错误的密码登录")l = LoginPage(self.driver)l.login_action("13838380000", "111111")result = l.is_toast_exist("密码错误")self.assertTrue(result)
Capabilities配置数据,desired_caps.yml
appActivity: .activity.MainActivity
appPackage: com.ss.android.article.news
deviceName: newDeviceName
platformName: Android
platformVersion: newPlatformVersion
automationName: UiAutomator2
unicodeKeyboard: true
resetKeyboard: true
noReset: true
ip: 127.0.0.1
port: 4723
测试用例test_login.py中,正确的账号、正确密码、错误密码也可以配置在Yaml文件中,即数据分离,使用时读取即可。
5、测试驱动层
执行测试模块,run.py
import time
import unittest
import HTMLTestRunnernow = time.strftime("%Y-%m-%d_%H_%M_%S")
report_dir = './report/'
fp = open(report_dir + now + "_report.html", 'wb')
runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title="App自动化测试报告",description="测试用例情况")test_dir='./testcase'
suite = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
runner.run(suite)
fp.close()
6、示例目录结构
运行run.py模块就能执行整个测试项目。
下面是我整理的2023年最全的软件测试工程师学习知识架构体系图 |
一、Python编程入门到精通
二、接口自动化项目实战
三、Web自动化项目实战
四、App自动化项目实战
五、一线大厂简历
六、测试开发DevOps体系
七、常用自动化测试工具
八、JMeter性能测试
九、总结(尾部小惊喜)
在追逐梦想的路上,不要畏惧失败与挫折,因为成功是奋斗的结晶。每一次努力都积累着成长的力量,坚持不懈的奋斗将为你带来辉煌的未来。
人生的意义在于奋斗,梦想的实现需要付出汗水和努力。不要被困难所吓倒,把每一次挑战都看作成长的机会,坚定信念,持之以恒,你的努力定能开创出耀眼的人生篇章。
在奋斗的道路上,唯有坚定的信念和不懈的努力,才能战胜困难、超越自我,让梦想的火焰燃烧不息。相信自己的潜力与价值,勇往直前,绽放属于你的辉煌人生。