本文是我在学习了selenium+Python进行自动化测试后的总结,方便以后回顾和填坑吧(确实遇到了不少坑~~),也希望各位大神能多多指教!
本文主要包含Page Object模式设计测试用例,selenium元素定位和常用操作,同时包含自己在实践中遇到的各种坑。
我设计的场景是用火狐浏览器登录百度首页,在首页上测试输入框,以及含有‘百度’的链接数量;然后点击“设置”–>”高级搜索”,进入搜索设置页面,检查下拉菜单和单选框的属性;然后在搜索框输入搜索词,在 搜索页面得到搜索数目。
PageObject是一种程序设计模式,将面向过程转变为面向对象(页面对象),将测试对象及单个的测试步骤封装在每个Page对象中,以page为单位进行管理。可以使代码复用,降低维护成本,提高程序可读性和编写效率。
PageObject可以将页面定位和业务操作分开,分离测试对象(元素对象)和测试脚本(用例脚本),提高用例的可维护性。
我理解的就是将Page和test分开,这样在一项改变时不会影响整体,变动减少。
首先看pages的设计,先有一个BasePage,这个BasePage包含所有待测page都能用到的公用方法。
验证页面的方法_validate_page()
使用了@abstractmethod
进行装饰,基类不能被实例化,只有当子类继承并且实现了改方法后子类才可以实例化,这样可以对不同的页面进行不同的验证。并定义了Search和Setting两种方法,用于转向不同页面,使用@property
将方法转为属性,方便实例化对象后使用。
对于首页的设计,如下:
在首页中,完成了对页面验证_validate_page()
的重写,并对含有’百度’的链接数和‘新闻’链接状态进行了返回。
对于搜索设置页面,如下:
在SettingRegion()类实现点击‘设置’进入设置页面,在SettingPage类里完成对该页面一些元素的检查,包括搜索时间范围的下来菜单,检查其包含的下来选项数量,选择其中一项,查找范围的单选按钮,选中一项。
对于搜索结果页面,如下:
主要完成输入搜索词点击搜索,SearchRegion()类,然后在搜索结果页面定位到结果语句,并将语句中的数值返回(即搜索到的条目数)。
我使用的是unittest来组织测试,pip install unittest
安装后,先在test的基类中完成测试前/后的方法,即setUp()和tearDown(),这会在每次test方法前后调用,若是想应用于整个测试类,可以使用类方法setUpClass()和tearDownClass(),并使用@classmethod
修饰。基类如下:
import unittest
from selenium import webdriver
class BaseTestCase(unittest.TestCase):
"""docstring for BaseTestCase"""
def setUp(self):
# create a new Firefox session
profile_dir = r'C:\Users\czhou012\AppData\Roaming\Mozilla\Firefox\Profiles\iewanxoz.default'
profile = webdriver.FirefoxProfile(profile_dir)
self.driver = webdriver.Firefox(profile)
self.driver.implicitly_wait(30)
self.driver.maximize_window()
# navigate to the application home page
self.driver.get('https://www.baidu.com/')
def tearDown(self):
# close the browser window
self.driver.quit()
最后写测试类,继承于BaseTestCase():
import unittest
from pages.homepage import HomePage
from pages.settingpage import SettingRegion
from base.basetestcase import BaseTestCase
from base.data import GetData
from ddt import ddt, data, unpack
@ddt
class PageTest(BaseTestCase):
"""docstring for PageTest"""
#@unittest.skip('not run')
def test_home_page(self):
hp = HomePage(self.driver)
self.assertEqual(4,hp.links_count)
self.assertTrue(hp.news_able)
#@unittest.skip('not run')
def test_set_page(self):
set_page = HomePage(self.driver).Setting.set_to()
self.assertEqual(5,set_page.num_show_time)
self.assertEqual('最近一天',set_page.select_one_day)
self.assertTrue(set_page.radio_select)
#@unittest.skip('no run ')
@data(*GetData().data)
@unpack
def test_search_result(self,term,expect_num):
sp = HomePage(self.driver).Search.searchFor(term)
self.assertGreater(sp.result_num,expect_num)
if __name__ == '__main__':
unittest.main(verbosity=2)
分别完成首页的测试,设置页面的测试,和搜索结果页面的测试。
在搜索结果的测试中我使用了数据驱动,即将数据参数化,适用于大量数据重复操作中,在这里我新建了一个csv数据文档,随意编写了搜索词条和必须大于的返回结果数目。
对应编写用于读取数据的模块:
#coding:utf-8
import csv
class GetData(object):
"""docstring for GetDat"""
def __init__(self, file_name=None):
if file_name != None:
self.file_name = file_name
else:
self.file_name = r'D:\TEST\selenium\my\testdata.csv'
# create an empty list to store rows
@property
def data(self):
rows = []
# open the CSV file
with open(self.file_name,'r',encoding='utf-8') as data_file:
# create a CSV Reader from CSV file
reader = csv.reader(data_file)
# skip the headers
next(reader, None)
# add rows from reader to list
for row in reader:
rows.append(row)
num_list = list(map(int,list(zip(*rows))[1]))
term_list = list(zip(*rows))[0]
return list(zip(term_list,num_list))
这样调用该对象的data即可返回数据[('phones', 3), ('music', 5), ('iphone 5s', 10)]
。
使用ddt模块pip install ddt
, 在测试类前加修饰@ddt
,在使用数据驱动的测试方法前使用@data(数据集)
,获取数据,使用@unpack
会自动将数据对应到多个参数上。单独运行可以看出结果:
去掉注释整体运行可得结果:
坑一:直接使用脚本启动Firefox浏览器,会发现没有浏览器插件,打开相同的网址,网页展示也不一样,这会给定位元素造成很大困扰。原因:用脚本去打开浏览器时候,其实是重新打开了一个进程,跟手动打开浏览器不是一个进程。解决方法:在启动时webdriver.Firefox(profile)传入参数profile,具体可参考此博文
坑二:对于需要悬停操作显示元素的定位:
百度首页的搜索设置选项需要在‘设置’悬停显示,需要加入鼠标移动到指定元素中央的操作。
坑三:选择下拉菜单选项时可能会有报错:‘Element could not be scrolled into view’,加入等待时间即可。
坑四:逐行读取csv文件后获得的二维列表中,Number列是str类型,这样不能直接比较,我这里采用的方法是将该列取出转为int,再和前一列合并。
坑五:我定位到的是百度搜索结果的一句话。需要在这句话中找出用逗号分隔表示的数值,这里没用正则表达式,而是使用isdigit()
来判断,需要注意返回的是类似于[‘1’,’2’,’3’,’4’]这样的列表,还需要拼接后再转为int类型。
新手学习,欢迎指教!