本文基于 python3 和 selenium3自动化测试框架,采用 Page Object 设计模式进行二次开发,通过对页面对象和测试代码进行分离,并封装了日志输出,浏览器引擎选择,二次封装常用方法。降低后期因页面变化带来的维护成本,减少了代码的重复,提高测试用例的可读性
注:这只是一个入门教程,关于selenium基础可以观看selenium教程,环境W10+pycharm
import logging
import os.path
import time
class Logger(object):
def __init__(self,logger):
# 创建一个logger
self.logger=logging.getLogger(logger)
self.logger.setLevel(logging.DEBUG)
# 创建一个handler,用于写入日志文件
rd=time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
log_path=os.path.dirname(os.path.abspath('.'))+'/logs/'
log_name=log_path+rd+'.log'
# 创建一个handler,用于写入日志文件
fh=logging.FileHandler(log_name)
fh.setLevel(logging.INFO)
# 创建一个handler,用于输出到控制台
ch=logging.StreamHandler()
ch.setLevel(logging.INFO)
# 定义handler的输出格式
formatter=logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 给logger添加handler
self.logger.addHandler(fh)
self.logger.addHandler(ch)
def getlog(self):
return self.logger
注:需要在自行根目录下创建logs文件夹接收日志输出,否则会提示找不到文件夹。
import os.path
from configparser import ConfigParser
from selenium import webdriver
from framework.logger import Logger
logger=Logger("BrowserEngine").getlog()
class BrowserEngine(object):
def open_browser(self):
#读取配置文件
config=ConfigParser()
dir=os.path.dirname(os.path.abspath('.'))
file_path=dir+'/config/config.ini'#此时需要在项目目录中创建config文件夹以及config.ini文件
config.read(file_path)
#读取配置文件属性
browser=config.get("browserType","browserName")
logger.info("You had select %s browser." % browser)
url=config.get("testServer","URL")
logger.info("The test server url is: %s" % url)
if browser == "Firefox":
self.driver=webdriver.Firefox()
logger.info("Starting firefox browser.")
elif browser == "Chrome":
self.driver=webdriver.Chrome(dir+'/tools/chromedriver.exe')#创建tools文件夹并放入驱动
logger.info("Starting Chrome browser.")
elif browser=="IE":
self.driver=webdriver.Ie(dir+'/tools/IEDriverServer.exe')
logger.info("Starting IE browser.")
self.driver.get(url)
logger.info("Open url: %s" % url)
self.driver.maximize_window()
logger.info("Maximize the current window.")
self.driver.implicitly_wait(10)
logger.info("Set implicitly wait 10 seconds.")
return self.driver
def quit_browser(self):
logger.info("Now, Close and quit the browser.")
self.driver.quit()
注:此时需要新建两个文件夹,config用来存储配置文件,tools用来放入浏览器驱动
import time
from framework.logger import Logger
import os.path
from selenium.common.exceptions import NoSuchElementException
logger=Logger('BasePage').getlog()
#定义一个页面基类,让所有页面继承这个类
class BasePage(object):
def __int__(self,driver):
self.driver=driver
def quit_browser(self):
self.driver.quit()
logger.info("browser is quit")
def forword(self):
self.driver.forword()
logger.info("Click forward on current page.")
def back(self):
self.driver.back()
logger.info("Click back on current page.")
def wait(self,seconds):
self.driver.implicitly_wait(seconds)
logger.info("wait for %d seconds." % seconds)
def close(self):
try:
self.driver.close()
logger.info("Closing and quit the browser.")
except NameError as e:
logger.error("Failed to quit the browser with %s" % e)
#截屏
def get_windows_imgs(self):
file_path=os.path.dirname(os.path.abspath('.'))+'/screenshots/'
rq=time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
screen_name=file_path+'rq'+'.png'
try:
self.driver.get_screenshot_as_file(screen_name)
logger.info("Had take screenshot and save to folder : /screenshots")
except NameError as e:
logger.error("Failed to take screenshot! %s" % e)
self.driver.get_windows_img()
#寻找元素方法
def find_element(self,*selector):#传入可变参数实现对元组的拆分
try:
element=self.driver.find_element(*selector)
logger.info("The element looked up is %s "% (selector[1]))
return element
except NoSuchElementException as e:
logger.error("NoSuchElementException: %s" % e)
self.get_windows_img()
#清除文本框
def clear(self,*selector):
el=self.find_element(*selector)
try:
el.clear()
logger.info("Clear text in input box before typing.")
except NameError as e:
logger.error("Failed to clear in input box with %s" % e)
self.get_windows_imgs()
#输入
def type(self,*selector,text):
self.clear(*selector)
el = self.find_element(*selector)
try:
el.send_keys(text)
logger.info("Had type \' %s \' in inputBox" % text)
except NameError as e:
logger.error("Failed to type in input box with %s" % e)
self.get_windows_imgs()
def click(self, *selector):
el = self.find_element(*selector)
try:
el.click()
logger.info("The element \' %s \' was clicked." % el.text)
except NameError as e:
logger.error("Failed to click the element with %s" % e)
#获得网页标题
def get_page_title(self):
logger.info("Current page title is %s" % self.driver.title)
return self.driver.title
@staticmethod
def sleep(seconds):
time.sleep(seconds)
logger.info("Sleep for %d seconds" % seconds)
注:创建screenshots文件夹以接收截图文件
from framework.base_page import BasePage
from selenium.webdriver.common.by import By
class homepage(BasePage):
input_box=(By.ID,'kw')
search_submit_btn=(By.XPATH,'//*[@id="su"]')
news_link=(By.XPATH,'//*[@id="url"]/a[@name="tj_trnews"]')
def type_search(self,text):
self.type(*self.input_box,text=text)#text为命名关键字参数必须传入参数名
def send_submit_btn(self):
self.click(*self.search_submit_btn)
def click_news(self):
self.click(*self.news_link)
# coding=utf-8
from framework.base_page import BasePage
class NewsHomePage(BasePage):
# 点击体育新闻入口
sports_link = "xpath=>//*[@id='channel-all']/div/ul/li[5]/a"
def click_sports(self):
self.click(self.sports_link)
self.sleep(2)
import time
import unittest
from framework.browser_engine import BrowserEngine
from pageobjects.baidu_homepage import HomePage
class BaiduSearch(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.browse=BrowserEngine()
cls.driver=cls.browse.open_browser()
@classmethod
def tearDownClass(cls):
cls.browse.quit_browser()
def test_baidu_search(self):
"""
这里一定要test开头,把测试逻辑代码封装到一个test开头的方法里。
:return:
"""
homepage=HomePage(self.driver)
homepage.type_search("苍井空")
homepage.send_submit_btn()
time.sleep(2)
homepage.get_windows_imgs()
try:
assert "苍井空" in self.driver.title
print("Test Pass")
except Exception as e:
print('Test Fail.',e)
def test_search2(self):
homepage = HomePage(self.driver)
homepage.type_search('python') # 调用页面对象中的方法
homepage.send_submit_btn() # 调用页面对象类中的点击搜索按钮方法
time.sleep(2)
homepage.get_windows_imgs() # 调用基类截图方法
print('Test Pass.')
def test_get_title(self):
homepage = HomePage(self.driver)
print(homepage.get_page_title())
if __name__ == '__main__':
unittest.main()
注:(setUp():每个测试case运行之前运行,tearDown():每个测试case运行完之后执行setUpClass():必须使用@classmethod 装饰器, 所有case运行之前只运行一次,tearDownClass():必须使用@classmethod装饰器, 所有case运行完之后只运行一次现在已经可以运行测试用例了)这里用pycharm直接运行会可能出现一些问题,我专门写了一篇文章记录,在我主页里
# coding=utf-8
import time
import unittest
from framework.browser_engine import BrowserEngine
from pageobjects.baidu_news_home import NewsHomePage
class BaiduSearch(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""
测试固件的setUp()的代码,主要是测试的前提准备工作
:return:
"""
browse = BrowserEngine()
cls.driver = browse.open_browser()
@classmethod
def tearDownClass(cls):
"""
测试结束后的操作,这里基本上都是关闭浏览器
:return:
"""
cls.driver.quit()
def test_baidu_news(self):
newspage = NewsHomePage(self.driver)
newspage.click_sports()
time.sleep(2)
newspage.get_windows_imgs() # 调用基类截图方法
print('Test Pass.')
if __name__ == '__main__':
unittest.main()
import unittest
from MyHTMLTestReportCN import HTMLTestRunner
import os
import time
import sys
sys.path.append("..")
# 定义输出的文件位置和名字
report_path = os.path.dirname(os.path.abspath('.')) + '/test_report/'
now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
HtmlFile = report_path+now+"HTMLtemplate.html"
fp = open(HtmlFile, "wb")
if __name__=='__main__':
suite = unittest.TestLoader().discover("testsuites")
runner = HTMLTestRunner(stream=fp, title='测试报告', description='执行情况',tester='XKS')
runner.run(suite)
注:需要新建文件夹test_report接收HTML报告