1、线性脚本开发
2、模块化脚本开发(封装线性代码到方法或者类中。在需要的地方进行调用)
3、关键字驱动开发:selenium ide关键字驱动开发
4、数据驱动开发:数据和脚本分离。数据专门存储在外部的文件中。(结合unittest框架的ddt(data driver test)模块)
5、po设计模式:将页面中的元素和操作的方法封装在一个页面对象中,只需要调用对象中封装的元素和操作方法来实现测试用例,不需要真实的页面
采用python+selenium+unitte实现自动化测试的基本脚本,还会涉及到ddt+json实现数据驱动,git和gitee实现持续集成。
# 采用unittest框架,实现正向注册的用例
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 单元测试用例
class mifeng_register(unittest.TestCase):
def setUp(self):
# 创建浏览器对象
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
# 打开某电商首页
self.driver.get('xxx')
def test_register(self):
'''正向注册功能测试用例'''
# # 打开蜜蜂电商首页
# self.driver.get('')
# 点击首页的“免费注册”按钮
timestr = time.strftime("%H%M%S")
username = "blue_" + timestr
email = username + "@163.com"
self.driver.find_element(By.LINK_TEXT, '免费注册').click()
# 输入正向的用户名
self.driver.find_element(By.ID, 'username').send_keys(username)
# 输入正向的邮箱
self.driver.find_element(By.ID, 'email').send_keys(email)
# 输入正向的密码
self.driver.find_element(By.ID, 'password').send_keys('123456')
# 输入正向的确认密码
self.driver.find_element(By.ID, 'repassword').send_keys('123456')
# 勾选协议
# 点击“立即注册”按钮
self.driver.find_element(By.LINK_TEXT, '立即注册').click()
# 强制等待,先等5s,跳过中间页面
time.sleep(5)
# 断言
sj = self.driver.find_element(By.XPATH, '/html/body/div[4]/div[1]/div[2]/div[1]/div[1]/h3/font[1]').text
yq = username
self.assertEqual(yq, sj, '预期结果和实际结果不一致')
def test_register_chongfu(self):
'''重复注册功能测试用例'''
# 打开电商首页
self.driver.get('xxx')
# 点击首页的“免费注册”按钮
self.driver.find_element(By.LINK_TEXT, '免费注册').click()
# 输入正向的用户名
self.driver.find_element(By.ID, 'username').send_keys('blue_002')
# 输入正向的邮箱
self.driver.find_element(By.ID, 'email').send_keys('[email protected]')
# 输入正向的密码
self.driver.find_element(By.ID, 'password').send_keys('123456')
# 输入正向的确认密码
self.driver.find_element(By.ID, 'repassword').send_keys('123456')
# 勾选协议
# 点击“立即注册”按钮
self.driver.find_element(By.LINK_TEXT, '立即注册').click()
# 强制等待,先等5s,跳过中间页面
time.sleep(5)
# 断言
# sj = self.driver.find_element(By.XPATH,'/html/body/div[4]/div[1]/div[2]/div[1]/div[1]/h3/font[1]').text
sj = self.driver.current_url
yq = r'xxx'
# yq='blue_002'
self.assertEqual(yq, sj, '预期结果和实际结果不一致')
def tearDown(self):
# 关闭浏览器
self.driver.quit()
if __name__ == '__main__':
unittest.main()
# 主运行文件:获取测试用例集合、运行测试用例集合、得到可视化测试报告....
import unittest
from BeautifulReport import BeautifulReport
import time
# 获取测试集合
# 获取级联(指定父目录,可以遍历子目录,要求目录必须是包)目录下的测试用例
discover = unittest.defaultTestLoader.discover(r'./TestCases/',pattern='mifeng*.py')
print(discover)
# 用时间戳作为测试报告的文件名
strTime = time.strftime('%m%d%H%M')
filename = 'report_'+strTime
# 创建运行器对象
runner = BeautifulReport(discover)
runner.report('电商自动化测试报告',filename,'./TestReport/')
# 采用unittest框架,实现正向登录的用例
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 单元测试用例
class mifeng_login(unittest.TestCase):
def setUp(self):
# 创建浏览器对象
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
# 打开电商首页
self.driver.get('xxx')
def test_login_01(self):
'''正常登录的测试用例'''
# self.driver.get('xxx')
self.driver.find_element(By.LINK_TEXT, '登陆').click()
# 输入用户名和密码(正向数据)
self.driver.find_element(By.ID, 'username').send_keys('blue_001')
self.driver.find_element(By.ID, 'password').send_keys('123456')
# 点击登陆按钮
self.driver.find_element(By.XPATH, '//*[@id="login-form"]/div/a').click()
# 断言
time.sleep(5)
sj = self.driver.find_element(By.XPATH, '/html/body/div[4]/div[1]/div[2]/div[1]/div[1]/h3/font[1]').text
yq = 'blue_001'
self.assertEqual(yq, sj, '预期结果和实际结果不一致!!!')
def test_login_02(self):
'''登录用户名为空的测试用例'''
# self.driver.get('xxx')
self.driver.find_element(By.LINK_TEXT, '登陆').click()
# 用户名设置为空
self.driver.find_element(By.ID, 'username').send_keys('')
self.driver.find_element(By.ID, 'password').send_keys('123456')
# 点击登陆按钮
self.driver.find_element(By.XPATH, '//*[@id="login-form"]/div/a').click()
# 断言
time.sleep(5)
sj = self.driver.find_element(By.XPATH, '//*[@id="login-form"]/div/dl[1]/dd/span/font').text
yq = '请输入用户名'
self.assertEqual(yq, sj, '预期结果和实际结果不一致!!!')
def test_login_03(self):
'''未注册用户名登录的测试用例'''
# self.driver.get('xxx')
self.driver.find_element(By.LINK_TEXT, '登陆').click()
# 用户名设置为空
self.driver.find_element(By.ID, 'username').send_keys('blue01')
self.driver.find_element(By.ID, 'password').send_keys('123456')
# 点击登陆按钮
self.driver.find_element(By.XPATH, '//*[@id="login-form"]/div/a').click()
# 断言
time.sleep(5)
sj = self.driver.current_url
yq = 'xxx'
self.assertEqual(yq, sj, '预期结果和实际结果不一致!!!')
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
unittest.main()
# 主运行文件:获取测试用例集合、运行测试用例集合、得到可视化测试报告....
import unittest
from BeautifulReport import BeautifulReport
import time
# 获取测试集合
# 获取级联(指定父目录,可以遍历子目录,要求目录必须是包)目录下的测试用例
discover = unittest.defaultTestLoader.discover(r'./TestCases/',pattern='mifeng*.py')
print(discover)
# 采用unittest框架,实现一个个人资料正常更新的用例
# 先登录--更新资料--退出
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from 电商项目1.Public.Basic import login,logout
# 单元测试用例
class mifeng_userinfo(unittest.TestCase):
def setUp(self):
# 创建浏览器对象
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
# 先登录
login(self.driver)
def test_userinfo_01(self):
'''正向个人资料修改功能测试用例'''
# 登录成功,改资料
# 点击个人资料超链接
self.driver.find_element(By.LINK_TEXT,'个人资料').click()
time.sleep(2)
# 设置昵称、QQ和性别、出生日期等
self.driver.find_element(By.ID, 'nickname').clear()
self.driver.find_element(By.ID,'nickname').send_keys('王同学')
self.driver.find_element(By.ID, 'qq').clear()
self.driver.find_element(By.ID,'qq').send_keys('2659160211')
self.driver.find_element(By.XPATH,'//*[@id="profile-form"]/dl[3]/dd/label[2]/input').click()
self.driver.find_element(By.XPATH,'//*[@id="birth_year"]/option[78]').click()
self.driver.find_element(By.XPATH,'//*[@id="birth_month"]/option[2]').click()
self.driver.find_element(By.XPATH,'//*[@id="birth_day"]/option[20]').click()
# 点击更新按钮
self.driver.find_element(By.CSS_SELECTOR,'#profile-form > div > button').click()
# 断言
sj = self.driver.find_element(By.XPATH,'/html/body/div/div[2]/h3').text
yq = '更新资料成功'
self.assertEqual(yq,sj)
def test_userinfo_02(self):
'''更新个人资料头像修改功能测试用例'''
# 登录成功,改资料
# 点击个人资料超键
self.driver.find_element(By.LINK_TEXT,'个人资料').click()
time.sleep(2)
# 提交头像
self.driver.find_element(By.NAME,'avatar_file').send_keys(r'd:/aa.jpg')
# 保存头像
self.driver.find_element(By.ID,'save-avatar-btn').click()
# 点击更新按钮
self.driver.find_element(By.CSS_SELECTOR,'#profile-form > div > button').click()
# 断言
sj = self.driver.find_element(By.XPATH,'/html/body/div/div[2]/h3').text
yq = '更新资料成功'
self.assertEqual(yq,sj)
def tearDown(self):
# 退出
logout(self.driver)
# 关闭浏览器
self.driver.quit()
if __name__=='__main__':
unittest.main()
对设计过的用例使用数据驱动的方式实现
数据驱动:由于数据的变化,而导致结果的不同(来实现不同的测试用例)的过程。
实现数据驱动:数据和脚本步骤分离式管理
数据的存储方式:
知识点:python+unittest+ddt+json来实现数据驱动
使用ddt实现数据驱动的步骤:
把测试用例执行情况记录在一个文件中,便于后续的查验。
步骤:
版本管理:多人协作、自动化脚本开发的时候,需要进行版本的维护
版本管理介绍
gitee注册
git的版本管理的常见命令
常见的工作区间
workspace:工作区,可以理解为你自己开发代码的文件夹,包含一个.git隐藏文件夹(git init)
staging area:暂存区/缓存区(临时存储文件的地方)
local repository:本地仓库
remote repository:远程仓库
常见的命令
配置SSH公钥
在gitee上创建一个空的仓库
Git 全局设置:
git config --global user.name "王同学"
git config --global user.email "[email protected]"
从本地上传脚本到远程仓库
先创建本地工作空间
添加工作空间下的文件到缓存区:
缓存区数据保存到本地仓库:git commit -m “v1”
本地仓库和远程仓库建立关联:git remote add origin gitee仓库项目地址
本地仓库上传到远程仓库:git push -u origin “master”
回到gitee,刷新仓库
mkdir blue-mi-feng
cd blue-mi-feng
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin gitee仓库项目地址
git push -u origin "master"
从远程下载到本地:git clone 地址
已经可以将自动化代码上传到gitee远程仓库进行管理了。
后续就可以直接从远程仓库,借助jenkins工具实现自动部署和运行(定时运行,触发运行)
作用和windows系统的环境变量配置很相似
jenkins+git实现自动化部署和执行,需要安装git和gitee插件,作相应的配置即可
1、需要安装git和gitee插件
2、配置git全局的环境配置
3、gitee中获取私人令牌
4、在jenkins中配置gitee参数
5、添加用户名和密码凭据
1、新建项目
2、配置git仓库
3、触发任务设置
4、添加window批处理构建
保存退出
5、运行任务
等待设置的定时时间
==POM(Page Object Model)==是一种自动化测试设计模式,它将页面对象和测试用例代码分离开来,使测试代码更加清晰和易于维护
POM模式的核心思想是将每个页面视为一个对象(类),并将页面的元素和操作封装在该对象中。测试代码只需要调用页面对象的方法,而不需要关心页面的具体实现细节。
POM模式的优点包括:
POM模式的实现步骤包括:
总之,POM模式是一种优秀的自动化测试设计模式,可以提高测试代码的可读性、可维护性、复用性和稳定性。在实际的自动化测试中,可以根据具体的需求和场景选择是否采用POM模式
# 实现封装webdriver api方法的,创建浏览器对象、页面打开、页面元素定位、点击、输入、获取文本、切换窗口、弹框、frame等
# 对开源软件webdriver的二次封装
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
class commondriver():
'''
功能:构造方法,实现的是变量的初始化
'''
def __init__(self):
# 可以先将self.driver变量看作是Chrome浏览器的对象
# 声明调用的对象,固定写法
self.driver: webdriver.Chrome = None
# 实现一个打开浏览器的方法,设计一个方法:给不同的参数,可以启动不同的浏览器对象
def open_browser(self, browser="chrome"):
'''
功能:实现一个打开浏览器的方法
:param browser: 可以是chrome、ff、firefox、safari、edge等
:return:暂时先不给,后面可以给self.driver
'''
if (browser == 'ff' or browser == 'firefox'):
self.driver = webdriver.Firefox()
elif (browser == 'edge'):
self.driver = webdriver.Edge()
else:
self.driver = webdriver.Chrome()
# 实现浏览器的最大化显示,页面元素的隐式等待
self.driver.maximize_window()
self.driver.implicitly_wait(10)
# 在多页面操作的时候,需要使用同一个页面对象
return self.driver
# 关闭浏览器对象
def close_browser(self):
'''
功能:关闭浏览器对象
:return:
'''
self.driver.quit()
# 打开指定的url地址页面,是需要参数url
def open_url(self, url=''):
'''
功能:打开指定的url地址
:param url: 页面地址
:return: 不需要
'''
if (url.startswith('http') or url.startswith('https')):
self.driver.get(url)
else:
print('输入的地址不正确,请重新输入!')
# 页面元素定位和操作:self.driver.find_element(By.ZZ,'xx').yy()
# 定义页面定位方法为私有的,只在本类内调用,其他不能调用
def __find_element(self, locator=''):
'''
功能:通过给定的定位器,来判断是哪一种定位方式(xpath\css\id\linktext)
:param locator: 是属性值
:return:element元素对象
'''
if (locator.startswith('/') or locator.startswith('//')):
# 符合上面条件的定位方式是xpath定位
element = self.driver.find_element(By.XPATH, locator)
elif (locator.startswith('.') or (locator.startswith('#') or locator.startswith('['))):
# 符合上面条件的定位方式是css定位
element = self.driver.find_element(By.CSS_SELECTOR, locator)
else:
# 默认是id
try:
element = self.driver.find_element(By.ID, locator)
except:
element = self.driver.find_element(By.LINK_TEXT, locator)
# 返回element页面元素对象
return element
# 封装文本框的输入方法(具备提前清空功能)
# 两个参数:定位器locator、输入的数据value
def input(self, locator='', value=''):
'''
功能:定位文本框,清空内容,输入文本
:param locator: 定位器
:param value: 输入的值
:return: 不需要
'''
element = self.__find_element(locator)
element.clear()
element.send_keys(value)
# 定义一个click点击方法
def click(self, locator=''):
'''
功能:定位元素并点击
:param locator:
:return:
'''
element = self.__find_element(locator)
element.click()
# 获取页面元素的文本信息
def get_text(self, locator=''):
'''
功能:获取页面元素的文本信息
:param locator:
:return: 返回获取的文本
'''
element = self.__find_element(locator)
return element.text
# switch_to的用法
def switch_to(self, type='window', locator=''):
'''
功能:切换窗口和frame
:param type:
:param locator:
:return:
'''
if (type == 'frame'):
element = self.__find_element(locator)
self.driver.switch_to.frame(element)
else:
handles = self.driver.window_handles
self.driver.switch_to.window(handles[-1])
# 待补充后续需要的方法即可
def get_current_url(self):
'''
获取页面的地址
:return:
'''
return self.driver.current_url
if __name__ == '__main__':
# 创建该类的对象
cd = commondriver()
cd.open_browser('')
# 打开百度页面
cd.open_url('https://www.baidu.com')
time.sleep(2)
# 定位百度文本框并输入CSDN叫我王同学
cd.input('kw', 'CSDN叫我王同学')
time.sleep(2)
# 点击百度一下按钮
cd.click('su')
time.sleep(2)
# 关闭浏览器
cd.close_browser()
# http://shop.mifeng.qfedu.com/
# 电商注册页面的一些特点:url、用户名、邮箱文本、密码、确认密码文本框、勾选复选框、立即注册按钮、登陆按钮、异常提示信息框
import time
from MiFengShopPO.common.commondriver import commondriver
# 封装注册页面类,使页面直接继承使用封装好的通用api方法
class RegisterPage(commondriver):
# 构造方法:只需要指定注册页面的url地址即可
def __init__(self,driver):
'''
功能:初始化注册页面的地址,直接绑定在类中
'''
# 接收外部一个统一的页面对象到当前页面
self.driver = driver
self.url = 'xxx'
# 封装注册页面上的用户名文本框:输入值
def input_username(self, value=''):
'''
功能:定位用户名文本框(直接指定使用id属性定位),并且绑定其输入功能
:param value: 输入的数据
:return:
'''
self.input('username', value)
def input_email(self, value=''):
'''
功能:定位邮箱文本框,并且绑定其输入功能
:param value: 输入的数据
:return:
'''
self.input('email', value)
def input_password(self, value=''):
'''
功能:定位密码文本框(直接指定使用id属性定位),并且绑定其输入功能
:param value: 输入的数据
:return:
'''
self.input('password', value)
def input_repassword(self, value=''):
'''
功能:定位确认密码文本框(直接指定使用id属性定位),并且绑定其输入功能
:param value: 输入的数据
:return:
'''
self.input('repassword', value)
def click_agree(self):
'''
功能:定位同意协议(id属性)并点击
:return:
'''
self.click('agree')
def click_registerNow(self):
'''
功能:定位立即注册(XPATH)并点击
:return:
'''
self.click('//*[@id="register-form"]/div/div[2]/a')
def click_login(self):
'''
功能:定位登陆按钮(XPATH)并点击,LINK_TEXT效率低
:return:
'''
self.click('/html/body/div[2]/div/div/div/div/a')
def get_username_text(self):
'''
功能:获取用户名对应的错误提示信息
:return:返回值是错误信息
'''
text = self.get_text('#register-form > div > dl:nth-child(1) > dd > span > font')
return text
def get_email_text(self):
'''
功能:获取邮箱对应的错误提示信息
:return:返回值是错误信息
'''
text = self.get_text('#register-form > div > dl:nth-child(2) > dd > span > font')
return text
def get_password_text(self):
'''
功能:获取密码对应的错误提示信息
:return:返回值是错误信息
'''
text = self.get_text('//*[@id="register-form"]/div/dl[3]/dd/span/font')
return text
def get_repassword_text(self):
'''
功能:获取确认密码对应的错误提示信息
:return:返回值是错误信息
'''
text = self.get_text('//*[@id="register-form"]/div/dl[4]/dd/span/font')
return text
if __name__ == '__main__':
# 先创建页面
rp = RegisterPage()
rp.open_browser()
rp.open_url(rp.url)
# 输入用户名为空
rp.input_username('')
# 输入邮箱
rp.input_email('[email protected]')
# 输入密码
rp.input_password('123456')
# 输入确认密码
rp.input_repassword('123456')
# 点击立即注册按钮
rp.click_registerNow()
time.sleep(1)
# 断言
yq = '请设置用户名'
sj = rp.get_username_text()
assert yq == sj
import time
# 反向的注册的用例,结合unittest框架
import unittest
from MiFengShopPO.pageobject.RegisterPage import RegisterPage
# 创建单元测试类
class mifeng_register_username(unittest.TestCase):
def setUp(self):
# 创建注册页面对象
self.rp = RegisterPage()
# 打开Chrome浏览器
self.rp.open_browser()
# 打开电商注册页面
self.rp.open_url(self.rp.url)
def test_register_username_01(self):
'''
用户名为空
:return:
'''
self.rp.input_username('')
self.rp.input_email('[email protected]')
self.rp.input_password('123456')
self.rp.input_repassword('123456')
self.rp.click_registerNow()
# 断言
time.sleep(5)
yq = '请设置用户名'
sj = self.rp.get_username_text()
self.assertEqual(yq,sj)
def test_register_username_02(self):
'''
用户名为test
:return:
'''
self.rp.input_username('test')
self.rp.input_email('[email protected]')
self.rp.input_password('123456')
self.rp.input_repassword('123456')
self.rp.click_registerNow()
# 断言
time.sleep(5)
yq = '用户名不符合格式要求'
sj = self.rp.get_username_text()
self.assertEqual(yq,sj)
def test_register_username_03(self):
'''
用户名为数字开头
:return:
'''
self.rp.input_username('123test')
self.rp.input_email('[email protected]')
self.rp.input_password('123456')
self.rp.input_repassword('123456')
self.rp.click_registerNow()
# 断言
time.sleep(5)
yq = '用户名不符合格式要求'
sj = self.rp.get_username_text()
self.assertEqual(yq,sj)
def test_register_username_04(self):
'''
用户名含特殊符号
:return:
'''
self.rp.input_username('ad#¥#¥#¥')
self.rp.input_email('[email protected]')
self.rp.input_password('123456')
self.rp.input_repassword('123456')
self.rp.click_registerNow()
# 断言
time.sleep(5)
yq = '用户名不符合格式要求'
sj = self.rp.get_username_text()
self.assertEqual(yq,sj)
def tearDown(self):
# 关闭浏览器
self.rp.close_browser()
if __name__ =='__main__':
unittest.main()
import unittest
from selenium import webdriver
import time
from BeautifulReport import BeautifulReport
discover = unittest.defaultTestLoader.discover('./testcases/register',pattern='mifeng_*.py')
filename = 'report_'+time.strftime('%m%d%H%M')
runner = BeautifulReport(discover)
runner.report('报告',filename,'./testreport/')
登录用例的实现:涉及到登录页面和中间跳转页面
import unittest
from MiFengShopPO.common.commondriver import commondriver
from MiFengShopPO.pageobject.LoginPage import LoginPage
from MiFengShopPO.pageobject.TiaoZhuanPage import TiaoZhuanPage
class mifeng_login(unittest.TestCase):
def setUp(self):
# 创建一个统一的浏览器对象
self.driver = commondriver().open_browser()
# 创建登录的对象,打开登录页面
self.lp = LoginPage(self.driver)
self.lp.open_url(self.lp.url)
def test_login_01(self):
'''
正向测试用例
:return:
'''
self.lp.input_username('blue_001')
self.lp.input_password('123456')
self.lp.click_login()
# 断言:采用跳转页面的元素做断言
self.tzp = TiaoZhuanPage(self.driver)
sj = self.tzp.get_tiaozhuan_text()
yq = '登录成功'
self.assertEqual(yq,sj)
def tearDown(self):
self.lp.close_browser()
if __name__=='__main__':
unittest.main()
import unittest
from MiFengShopPO.common.commondriver import commondriver
from MiFengShopPO.pageobject.LoginPage import LoginPage
from MiFengShopPO.pageobject.TiaoZhuanPage import TiaoZhuanPage
class mifeng_login(unittest.TestCase):
def setUp(self):
# 创建一个统一的浏览器对象
self.driver = commondriver().open_browser()
# 创建登录的对象,打开登录页面
self.lp = LoginPage(self.driver)
self.lp.open_url(self.lp.url)
def test_login_01(self):
'''
正向测试用例
:return:
'''
self.lp.input_username('blue_001')
self.lp.input_password('123456')
self.lp.click_login()
# 断言:采用跳转页面的元素做断言
self.tzp = TiaoZhuanPage(self.driver)
sj = self.tzp.get_tiaozhuan_text()
yq = '登录成功'
self.assertEqual(yq,sj)
def tearDown(self):
self.lp.close_browser()
if __name__=='__main__':
unittest.main()
```