Web自动化测试(全篇-疑难-搭建-集成)

一、元素定位以及可能出现的问题

1、简单的元素定位

打开页面后,第二件事情就是定位到我们要操作的页面元素,定位单个页面元素有如下方法(下面的方法,如果没有定位到相应的元素,抛出NoSuchElementException异常):

  • find_element_by_id:通过id属性定位元素(返回第一个匹配的)。
  • find_element_by_name:通过name属性定位元素(返回第一个匹配的)。
  • find_element_by_xpath:通过xpath定位元素(返回第一个匹配的)。
  • find_element_by_link_text:通过超链接文本定位超链接元素,必须是完全匹配(返回第一个匹配的)。
  • find_element_by_partial_link_text:通过超链接文本定位超链接元素,可以是部分匹配(返回第一个匹配的)。
  • find_element_by_tag_name:通过标签名字定位元素(返回第一个匹配的)。
  • find_element_by_class_name:通过class属性定位元素(返回第一个匹配的)。
  • find_element_by_css_selector:使用CSS选择器语法定位元素(返回第一个匹配的)。

此外,webdriver还有find_elementfind_elements方法定位元素:

from selenium.webdriver.common.by import By

driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')

ID :元素id属性。
XPATH:xpath。
LINK_TEXT :超链接文本。
PARTIAL_LINK_TEXT :部分超链接文本。
NAME :元素name属性。
TAG_NAME :元素标签名字。
CLASS_NAME :元素class属性。
CSS_SELECTOR :使用CSS元素选择器语法。

2、部分难以定位的元素

(1)frame/iframe表单嵌套

        解决方法:driver.switch_to.frame()

driver.switch_to.parent_frame() 切回到父frame

driver.switch_to.default_content() 跳回最外层的页面

a = ff.find_element_by_tag_name("a")

# 保存原始窗口,window_handlers是目前wd打开的所有窗口的句柄列表
prev = ff.window_handles[-1]

# 点击超链接(targe="_blank")后,浏览器新窗口被激活
a.click()

# 保存新窗口
new = ff.window_handles[-1]

# 切换到原始窗口
ff.switch_to_window(prev)
print "Switch to prev success"

# 切换到新窗口
ff.switch_to_window(new)
print "Switch to new success"
(2)页面跳转到新的标签页(窗口切换)

         解决方法driver.switch_to.window(window_handle)切换到新窗口。

windows = self.driver.window_handles
        self.driver.switch_to.window(windows[1])
#或者for循环遍历
for handle in driver.window_handles:
    # 先切换到该窗口
    wb.switch_to.window(handle)
    # 得到该窗口的标题栏字符串,判断是不是要操作的窗口
    if '窗口标题' in driver.title:
        # 如果是,那么WebDriver对象就是对应的该窗口,跳出循环
        break
(3)弹出警告框(alert/confim/prompt)

        解决方法:alert = driver.switch_to.alert,其中

alert窗口:提示用户信息仅有确认按钮
confirm窗口:有确认和取消按钮
prompt窗口:有输入框、确认和取消按钮
text: 返回(获取)alert/confirm/prompt中的文字信息
accept():接受现有警告框
dismiss(): 放弃现有警告框
send_keys(keys_ToSend):发送文本至警告框

#切换到alert
        alert = self.driver.switch_to.alert
        print(alert.text)
        sleep(2)
        alert.accept()
#confirm弹框
        self.driver.find_element_by_id('confirm').click()
        confirm = self.driver.switch_to.alert
        print(confirm.text)
        sleep(3)
        #确定
        # confirm.accept()
        #取消
        confirm.dismiss()
#prompt弹框
        self.driver.find_element_by_id('prompt').click()
        prompt = self.driver.switch_to.alert
        print(prompt.text)
        prompt.send_keys('20')
        sleep(3)
        prompt.accept()
(4)元素动态变化

         解决方法:通过XPATH来定位,driver.find_element( By.XPATH, "" )

(5)页面元素失去焦点导致脚本运行不稳定

        解决方法driver.switch_to.active_element

(6)页面没有加载出来,对页面元素操作

        解决方式:三种等待

#1、WebDriverWait() 显示等待。等待单个的元素加载,通常配合until()、until_not()方法使用。 
WebDriverWait(driver, 10,0.5).until(driver.find_element( By.CSS_SELECTOR,""))

#2、time.sleep( ) 强制等待。当执行下一句代码,这种等待方式时间到了就执行下一个语句,但不能保证在等待的时间内元素真正被加载了出来。
time.sleep(10) # 表示强行等待10s

#3、driver.implicitly_wait() 隐式等待。表示在规定的时间内页面的所有元素都加载完了就执行下一步,否则一直等到时间截止,然后再继续下一步。
driver.implicitly_wait(30) #等待30s
(7)点击 display:none元素解决方法
<div>
	<div class="login_btn" style="background: url(&quot;/static/images_login/btn_sign_in_normal.png&quot;) 0% 0% / 100% 100%;"></div>
	<div class="login_btn" style="background: url(&quot;/static/images_login/btn_sign_in_pressed.png&quot;) 0% 0% / 100% 100%; display: none;"></div>
</div>

登录按钮是一个图片,移动到元素上有效果,开发的代码逻辑是通过hover改变display的block和none来实现效果。

display:none——隐藏元素并脱离文档流,即隐藏时不占用空间。

在我用selenium去点击的时候就出现了点击不了的情况

通过键盘的enter点击。
这里使用的是键盘事件Key,ENTER,这种方法适用于绑定了ENTER事件的情况

#部分代码
from selenium.webdriver.common.keys import Keys

driver.find_element_by_xpath("//div[2][@class ='login_btn']").send_keys(Keys.ENTER) 

通过执行JS代码让元素显示出来(上述问题通过本方法解决)
通过自带的方法execute_script来执行JS让元素显示出来

#部分代码
js="document.getElementsByClassName('login_btn')[1].style.display='block'"
driver.execute_script(js)
driver.find_element_by_xpath("//div[2][@class='login_btn']").click()

二、对定位的元素进行操作

1、一般定位的元素进行操作

文本框:input

  • sendkeys();//输入内容
  • clear();//清除内容
  • getAttribute 获取元素属性

# 通过输入框的id定位输入框元素
input_box = driver.find_element(By.ID, 'inputBoxId')

# 向输入框输入内容
input_box.send_keys('Hello, World!')

# 清除输入框内容
input_box.clear()

# 获取输入框的value属性
value = input_box.get_attribute('value')

单选框 radio 复选框 checkbox

  • click
  • clear (清除选中状态)
  • isSelected 查看是否被选中
# 单选框操作示例
radio_button = driver.find_element(By.ID, 'radioButtonId')
# 点击单选框
radio_button.click()
# 判断单选框是否被选中
is_selected = radio_button.is_selected()
print("Is radio button selected? " + str(is_selected))

# 复选框操作示例
checkbox = driver.find_element(By.ID, 'checkboxId')
# 点击复选框
checkbox.click()
# 判断复选框是否被选中
is_selected = checkbox.is_selected()
print("Is checkbox selected? " + str(is_selected))

按钮 button / 超链接 a

  • click
  • isEnabled 查看是否可以操作
# 按钮操作示例
button = driver.find_element(By.ID, 'buttonId')
# 判断按钮是否可操作
is_enabled = button.is_enabled()
print("Is button enabled? " + str(is_enabled))
# 点击按钮
button.click()

2、复杂定位的元素进行操作

组合键操作

一般使用click方法,如果是chrome浏览器,点击超链接 同时按ctrl会打开新标签,shift打开新的窗口。


Actions a = new Actions(driver);//点击按下
a.keyDown(Keys.CONTROL).perform();
driver.findElement(By.xpath("")).click();
a.keyUp(Keys.CONTROL).perform();//点解结束
组合鼠标操作

 鼠标左键点击 (可以来打开二级菜单)

Actions action = new Actions(driver);action.click();// 鼠标左键在当前停留的位置做单击操作  
action.click(element)// 鼠标左键点击指定的元素 
action.contextclick(element)//;鼠标右键点击指定元素
action.doubleClick(element);//鼠标双击元素

鼠标移动操作 (可以来打开二级菜单)

 action.moveToElement(element);// 将鼠标移到 element 元素中点 
 action.moveToElement(element,xOffset,yOffset) 
 //表示以当前element相对移动的位移量

鼠标拖拽动作

action.dragAndDrop(source,target); //将source元素拖拽到target
action.dragAndDrop(source,xOffset,yOffset); 
//可以由以上简单操作组合
action.clickAndHold(source).moveToElement(target).perform();   action.release(); 
//或者
action.clickAndHold(source).moveToElement(target).release().build().perform();
alert对话框处理
  • alert confirm相同
Alert alert = driver.switchTo().alert();
alert.accept();
alert.dismiss();
alert.getText(); //获取alert的内容
  • prompt (有提示内容和输入框)
获取alert相同
通过sendKeys()输入内容
select菜单,选择项

定位下拉:element=driver.find_element(By....)

把找到的页面元素,转换为下拉框的类型Select

select = Select(element)

调用Select类中的select_by_*方法

.select_by_value(选项的value属性的值)

.select_by_index(第几个选项)

.select_by_visible_text(选项的文本值)

# 采用xpath获取第一个select元素(即使有多个也返回第一个)
element = ff.find_element_by_xpath("//select[@name='car']")
# 获取所有选项,打印选项值并点击
all_options = element.find_elements_by_tag_name("option")
for option in all_options:
    print "Value is: %s" % option.get_attribute("value")
    option.click()

from selenium.webdriver.support.ui import Select
select = Select(ff.find_element_by_xpath("//select[@name='car']"))
select.select_by_visible_text("infinity")
select.select_by_value("bmw")
# 按照option的索引选择,第一个option的index为0
select.select_by_index("0")
Window/Tag切换
driver.getWindowHandle();返会字符串,获取当前窗口的句柄
driver.getWindowHandles();返回Set<String> 获取所有的窗口
driver.switchTo().window(窗口的String); 来跳转窗口(无论是新的标签还是新的浏览器窗口都可以通过此方法跳转)。

 注意窗口间切换,要关闭窗口时,不能使用 qiut(quit driver就释放了,不能使用driver,close只是关闭当前window 或者 标签 还可以使用driver,当然close最后一个窗口或者标签时就会关闭浏览器 此时driver也不会有效)

滚动定位到特定元素位置
JavascriptExecutor scroll = (JavascriptExecutor) driver;
scroll.executeScript("arguments[0].scrollIntoView();", 指定元素);

3、js操作

js的查找元素方法(ID-对此进行类推) 

document.getElementById("id")

 execute_script()
从上可以看出其实js的定位元素方法和selenium中的差不多,接下来我们就可以将需要执行的js语句放入到执行函数中使用。

from selenium import webdriver


browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
# 定位后修改指定元素的value属性
js_script_exec = 'document.getElementById("form_motion").value="list_modify";'
browser.execute_script(js_script_exec)

4、注意点与小技巧 

对于某些动态div标签(窗口),一般的方法不太奏效的情况下,可以尝试下switch_to_default_content()方法,跳转到最外层;

使用模拟键鼠操作的时候,无论是单独使用还是链式写法,记得在结尾加上perform()方法进行执行;

如果元素定位时报错element click intercepted,记得检查界面上是否有其他元素进行覆盖,元素有可能也是具有隐藏属性的;

元素过期报错element is not attached to the page document,可以尝试重新刷新页面,这里不推荐直接使用refresh方法,还是养成好习惯先怼上try…except…捕捉到异常后在进行刷新或重置操作;

对于属性值为动态的元素,墙裂推荐使用CSS selector或者xpath方法来进行元素定位,正则表达式也推荐大家最好能掌握;

如果前期对xpath的相对路径写法比较头疼,推荐使用F12调试工具自带的元素复制功能,在你想要复制的元素所在的标签对这行右键,选择copy —— Copy XPath选项即可;

输入框默认存在内容想要删除再输入信息的话,不推荐模拟键盘操作Ctrl+A,然后模拟退格键,试试clear()方法吧;

抓不到元素可以使用延时方法,输入文字也是一样的道理,业务场景中需要大量输入文字的,无论是从文件中还是提取又或者是遍历,出现少字漏字的话,同样也可以使用延时的方法,适当的放慢处理的速度;

在页面中总会有些不可见的元素,这种情况使用is_displayed()方法即可快速定位找到;

有些被测页面需要验证码,无论是手机的还是图片验证,和开发同学沟通一下,留个万能的就行了,其本身的功能手工回归一下即可,不必太过纠结;(之前用过的ddddocr库来解析验证码,还可以)

三方登录功能也是如此,不推荐直接使用web自动化去搞,三方的一般是不开源的,有这折腾的时间还不如跑跑接口和黑盒,自动化的话绕过去即可;

三、对代码进行封装(po模型)

1.POM设计模式

pom指的是page object module,po模式,也就是说会把每个页面当做一个对象来对待,而该页面下的动作都会封装在这个对象中。通过组装页面对象的操作来完成业务的封装。在编写测试用例时调用对应的业务完成测试即可。

2.搭建web框架结构

主要是围绕po模式来搭建结构

pages: 这是一个package,主要用来管理和存放页面对象的封装

common: 这是一个package,主要用来管理和存放底层代码,比如driver的封装、日志封装、文

件封装

logs: 这是一个目录,存放日志文件的

report: 这是一个目录,存放测试结果以及测试报告的

video:这是一个目录,是测试过程中临时存放截图的

actions:这是一个package,主要用来管理和存放业务的封装

testcases:这是一个package,主要用来管理和存放测试用例的封装

page层:把每个单独的页面独立出来,管理自己页面中的元素的基本操作
page里面的页面文件是随时都在增加的,也就是在做业务流程时都需要用到哪些页面就会把哪些页面封装在page里面,所以说它是随着业务层在增加的。

3、封装的示例代码

基础类封装

class BasePage:

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


    # 打开页面
    def into_testin(self,url):
        self.driver.get(url)

    # 定位元素
    def locate_element(self,args):
        return self.driver.find_element(*args)

    #定位一组元素
    def locate_eles(self,args):
        return self.driver.find_elements(*args)

    #输入值
    def input_(self,args,text):
        self.locate_element(args).send_keys(text)

    #点击按钮
    def click_button(self,args):
        self.locate_element(args).click()
页面对象类

页面对象层:pageobject,存放页面的元素定位和操作流程
登录模块代码实现:

from time import sleep
from basepage.base_page import BasePage
from selenium.webdriver.common.by import By


class LoginPage(BasePage):

    '''定位页面元素'''
    
    #url
    testin_url='https://www.testin.cn/account/login.htm'
    
    #输入用户名和密码
    username_loc=(By.ID,'email')
    password_loc=(By.ID,'pwd')

    #点击登录按钮
    login_button=(By.ID,"submitBtn")

    #登录邮箱操作流程
    def testin_login(self,username='18170710339',password='Quyun1230'):
        self.into_testin(self.testin_url)
        sleep(2)
        self.locate_element(self.username_loc).send_keys(username)
        sleep(1)
        self.locate_element(self.password_loc).send_keys(password)
        sleep(1)
        self.click_button(self.login_button)
封装登录页login.py

注意:这里的login.py和业务层action里的login.py不是一个py文件,这是对登录页元素的封装,相当于是对整个页面的具体封装文件

class LoginPage(BuyerPage):
    '''
    首页 元素进行操作
    定位元素   点击 输入操作 driver对象- driver.py 进行封装InitDriver类
    '''

    # def __init__(self,driver:InitDriver):
    #     # 后面很多方法都要用到这个driver,把他设置为属性
    #     self.driver = driver
    #     # 思路百度: 动态获取类名
    #     self.page_info = read_yaml('/pagefiles/buyer.yml').get('LoginPage')
    def username_login(self):
        ele_info = self.page_info.get('账号登录')
        self.driver.click(ele_info)

    def input_name(self, name):
        ele_info = self.page_info.get('输入用户名')
        self.driver.send_keys(ele_info, name)

    def input_password(self, password):
        ele_info = self.page_info.get('输入密码')
        self.driver.send_keys(ele_info, password)

    def input_valicode(self):
        ele_info = self.page_info.get('输入验证码')
        #这里把验证码写死了,真实环境中可以通过ddddocr来实现OCR识别获取验证码
        self.driver.send_keys(ele_info, '1512')

    def login_button(self):
        ele_info = self.page_info.get('点击登录按钮')
        self.driver.click(ele_info)
测试用例层

测试用例层:存放测试用例及数据

import pytest
from time import sleep
from selenium import webdriver
from common.excel import read_excel
from pageobject.login_page import LoginPage
from pageobject.select_phone import SelectPage

class TestTestIn:

    #打开浏览器
    def setup(self) -> None:
        self.driver=webdriver.Chrome()
        driver=self.driver

    def teardown(self) -> None:
        sleep(1)
        self.driver.close()

    '''利用excel导入登录测试数据'''
    @pytest.mark.parametrize('case',read_excel('./data/data.xlsx','login'))
    def test_01_login(self,case):
        '''测试登录模块'''
        xh,case_name,username,password,is_exc,result,bz=case
        lp=LoginPage(self.driver)
        lp.testin_login(username,password)

    def test_02_select_iphone_12(self):
        '''测试根据品牌选择手机'''
        lp = LoginPage(self.driver)
        lp.testin_login()
        ps=SelectPage(self.driver)
        ps.testin_select_01_iphone()


    def test_03_select_androi_sys(self):
        '''测试根据安卓系统选择手机'''
        lp = LoginPage(self.driver)
        lp.testin_login()
        ps=SelectPage(self.driver)
        ps.testin_select_02_android()

    def test_04_select_online_time(self):
        '''测试根据上市时间来选择手机'''
        lp = LoginPage(self.driver)
        lp.testin_login()
        ps=SelectPage(self.driver)
        ps.testin_select_03_onlin_time()

创建读取excel目录

创建一个common目录,再创建一个read_excel的文件:

import openpyxl

def read_excel(excel_dir,sheet_name):
    '''读取excel'''

    #加载目录
    ex=openpyxl.load_workbook(excel_dir)

    #获取sheet页
    sheet=ex[sheet_name]

    #打印表最大行和列
    # print(sheet.max_row,sheet.max_column)
    # print(sheet.cell(2,1).value)
    #循环行和列
    sheet_list=[]
    for row in range(2,sheet.max_row+1):
        row_list=[]
        for col in range(1,sheet.max_column+1):
            row_list.append(sheet.cell(row,col).value)
        sheet_list.append(row_list)
    return sheet_list

if __name__ == '__main__':
    read_excel('../data/data.xlsx','login')
封装首页home_page.py

像淘宝或者京东这类的网上商城一样,首页有搜索框,登录,注册这类的按钮元素,业务场景中需要首页的哪些元素就对他进行响应的封装即可

class HomePage(BuyerPage):
    '''
    首页 元素进行操作
    定位元素   点击 输入操作 driver对象- driver.py 进行封装InitDriver类
    '''
    def login_link(self):
        # ele_info = {'type': name / xpath / id / css / classname / tagname / linktext / plinktext, 'value': 值}
        ele_info = self.page_info.get('登录链接')
        self.driver.click(ele_info)
        return LoginPage(self.driver)

    def perseon_center(self):
        ele_info = self.page_info.get('进入个人中心')
        self.driver.click(ele_info)
        return PersonCenterPage(self.driver)

    def input_search_text(self, text):
        ele_info = self.page_info.get('搜索框')
        self.driver.send_keys(ele_info, text)

    def click_search_btn(self):
        ele_info = self.page_info.get('搜索按钮')
        self.driver.click(ele_info)
        return SearchResultPage(self.driver)
封装个人中心页personcenter_page.py

一般都是登录之后会展示个人中心按钮

class PersonCenterPage(BuyerPage):

    def cllick_acceptaddress(self):
        # ele_info = {'type': name / xpath / id / css / classname / tagname / linktext / plinktext, 'value': 值}
        ele_info =self.page_info.get('收货地址菜单')
        self.driver.click(ele_info)
        return AcceptAddressPage(self.driver)

封装添加地址页add_adderss_page.py
添加收货地址需要用到收货人姓名、联系地址、收货区域、详细地址以及地址别名(设为默认可以暂时不用管),需要对这些元素一一封装,特别注意的是收货地址未选择下拉框,需要用到鼠标悬停的动作,

class AcceptAddressPage(BuyerPage):

    def add_address(self):
        # ele_info = {'type': name / xpath / id / css / classname / tagname / linktext / plinktext, 'value': 值}
        ele_info =self.page_info.get('添加地址')
        self.driver.click(ele_info)
    def acceptgoods_name(self,name):
        ele_info = self.page_info.get('收货人姓名')
        self.driver.send_keys(ele_info,name)
    def acceptgoods_tel(self,tel):
        ele_info = self.page_info.get('输入联系方式')
        self.driver.send_keys(ele_info, tel)
    def choose_receaddress(self,provice,city,area):
        ele_info=self.page_info.get('收货地区')
        # todo 鼠标悬浮-需要在driver.py进行增加
        # 省
        time.sleep(2)
        ele_info = {'name':'省','type':'linktext','value':provice}
        self.driver.click(ele_info)
        time.sleep(2)
        ele_info = {'name':'市','type':'linktext','value':city}
        self.driver.click(ele_info)
        time.sleep(2)
        ele_info = {'name':'区','type':'linktext','value':area}
        self.driver.click(ele_info)

如果网不好的话可以加死等(time.sleep())的方式解

运行用例:

创建一个run_test.py文件:

import os
import pytest
from time import sleep

if __name__ == '__main__':
    pytest.main()
    sleep(2)
    os.system('allure generate ./temp -o ./reports --clean')

查看测试报告

查看reports目录中的html测试,用浏览器打开:

四、jenkins集成 

 启动jenkins

cmd中进入jenkins目录,执行命令:

java -jar jenkins.war

创建任务 

进入jenkins,创建一个任务:

添加项目目录:

设置运行命令:

 保存,创建成功。
任务创建成功后, 执行运行操作。

可以看到已经在运行了。

 运行成功。测试报告,同上,就不再次看了。

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Daniel Hao(找工作中)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值