1 项目背景
https://passport.csdn.net/login CSDN登录页面
2 功能实现
· 自动运行用例
· 自动生成测试报告
· 自动断言与截图
· 自动将最新测试报告发送到指定邮箱
· 数据,页面元素分离
· PageObject+Unittest+ddt数据驱动用例
· 执行日志、分布式执行
3 项目架构
4 浏览器Driver定义
from common.readFile import ReadFile
from common.logger import Logger
from selenium import webdriver
logger = Logger()
from selenium.webdriver import Remote
class Browser():
def __init__(self):
config = ReadFile()
self.browser = config.readConfig("Browser", "browser")
self.host = config.readConfig("host","host")
logger.info("You had select {} host {} browser.".format(self.host,self.browser))
def driver(self):
"""
启动浏览器驱动
:return: 返回浏览器驱动URL
"""
try:
# driver = webdriver.Chrome()
driver = Remote(command_executor='http://' + self.host + '/wd/hub',
desired_capabilities={ 'platform': 'ANY',
'browserName': self.browser,
'version': "",
'javascriptEnabled': True
}
)
return driver
except Exception as msg:
print("驱动异常-> {0}".format(msg))
5 用例运行前后的环境准备工作
import unittest
from common.driver import Browser
class StartEnd(unittest.TestCase):
def setUp(self):
self.driver = Browser().driver()
self.driver.implicitly_wait(10)
self.driver.maximize_window()
def tearDown(self):
self.driver.quit()
6 工具方法模块
主要封装一些公共的方法如:截图,查找最新报告。
import time
from selenium import webdriver
import os,sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from config import setting
def inser_img(driver):
# 指定截图存放的根目录路径
screen_dir = setting.TEST_REPORT + '/imges/'
rq = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
screen_name = screen_dir + rq + '.png'
driver.get_screenshot_as_file(screen_name)
print('screenshot:' + screen_name)
#查找最新的测试报告
def latest_report(report_dir):
lists = os.listdir(report_dir)
lists.sort(key=lambda fn: os.path.getatime(report_dir + '\\' + fn))
file = os.path.join(report_dir, lists[-1])
return file
def latest_report_img(report_dir):
lists = os.listdir(report_dir)
lists.sort(key=lambda fn: os.path.getatime(report_dir + '\\' + fn))
file = os.path.join(report_dir, lists[-1])
return file
7 Pageobject页面对象封装
基础页面类
import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from common.logger import Logger
from common.readFile import ReadFile
logger = Logger()
class BasePage():
"定义一个页面基类,让所有页面都继承这个类,封装一些常用的页面操作方法到这个类"
def __init__(self, driver):
self.driver = driver
config = ReadFile()
self.baseurl = config.readConfig("BaseUrl", "url")
def open_url(self, url):
self.driver.get(self.baseurl + url)
# 退出浏览器
def quit_browser(self):
self.driver.quit()
# 浏览器前进操作
def forward(self):
self.driver.forward()
# 浏览器后退操作
def back(self):
self.driver.back()
# 隐式等待
def wait(self, seconds):
self.driver.implicitly_wait(seconds)
# 查找元素
def find_element(self, selector):
selector_by = selector['find_type']
selector_value = selector['element_info']
try:
if selector_by == 'id':
el = self.driver.find_element_by_id(selector_value)
elif selector_by == "n" or selector_by == 'name':
el = self.driver.find_element_by_name(selector_value)
elif selector_by == 'cs' or selector_by == 'css_selector':
el = self.driver.find_element_by_css_selector(selector_value)
elif selector_by == 'cn' or selector_by == 'classname':
el = self.driver.find_element_by_class_name(selector_value)
elif selector_by == "lt" or selector_by == 'link_text':
el = self.driver.find_element_by_link_text(selector_value)
elif selector_by == "plt" or selector_by == 'partial_link_text':
el = self.driver.find_element_by_partial_link_text(selector_value)
elif selector_by == "tn" or selector_by == 'tag_name':
el = self.driver.find_element_by_tag_name(selector_value)
elif selector_by == "x" or selector_by == 'xpath':
el = self.driver.find_element_by_xpath(selector_value)
elif selector_by == "ss" or selector_by == 'selector_selector':
el = self.driver.find_element_by_css_selector(selector_value)
else:
raise NameError("Please enter a valid type of targeting elements.")
except NoSuchElementException :
logger.error("{0}页面中未能找到{1}元素".format(self, selector_value))
return el
# 输入
def input(self, selector, text):
el = self.find_element(selector)
try:
el.clear()
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)
# 点击
def click(self, selector):
el = self.find_element(selector)
try:
logger.info("The element \' %s \' was clicked." % el.text)
el.click()
except NameError as e:
logger.error("Failed to click the element with %s" % e)
@staticmethod
def sleep(seconds):
time.sleep(seconds)
logger.info("Sleep for %d seconds" % seconds)
def get_text(self,selector):
el = self.find_element(selector)
try:
return el.text
except NameError as e:
logger.error("Failed to text the element with %s" % e)
def switch_frame(self, selector):
"""
多表单嵌套切换
:param loc: 传元素的属性值
:return: 定位到的元素
"""
try:
el = self.find_element(selector)
return self.driver.switch_to_frame(el)
except NoSuchElementException as e:
logger.error("查找iframe异常-> {0}".format(e))
def switch_windows(self, selector):
"""
多窗口切换
:param loc:
:return:
"""
try:
el = self.find_element(selector)
return self.driver.switch_to_window(el)
except NoSuchElementException as e:
logger.error("查找窗口句柄handle异常-> {0}".format(e))
def switch_alert(self):
"""
警告框处理
:return:
"""
try:
return self.driver.switch_to_alert()
except NoSuchElementException as e:
logger.error("查找alert弹出框异常-> {0}".format(e))
LoginPage.py —— CNDS登录页面
from pageObject.basePage import *
from selenium import webdriver
from common.readFile import ReadFile
from config import setting
login_el = ReadFile().readYaml(setting.TEST_Element_YAML + '/' + 'login.yaml')
data = ReadFile().readYaml(setting.TEST_DATA_YAML + '/' + 'login_data.yaml')
class CndsPage(BasePage):
'''登录页面'''
url = '/login'
# 定位器,通过元素属性定位元素对象
#选择账号密码登录
chanlelogin_loc = login_el['testcase'][0]
# 账号输入框
username_loc = login_el['testcase'][1]
# 密码输入框
pwd_loc = login_el['testcase'][2]
# 单击登录
login_accout_loc = login_el['testcase'][3]
def accout_login(self,accout,passwd):
self.open_url(self.url)
self.click(self.chanlelogin_loc)
self.input(self.username_loc,accout)
self.input(self.pwd_loc,passwd)
self.click(self.login_accout_loc)
# 定位器,通过元素属性定位检查项元素对象
user_login_success_loc = login_el['check'][0]
accout_id_loc = login_el['check'][1]
accout_pawd_error_loc = login_el['check'][2]
# 账号或密码错误提示
def accout_passwd_error(self):
return self.get_text(self.accout_pawd_error_loc)
# 登录成功,跳转到个人资料页,获取用户名
def get_account(self):
self.click(self.user_login_success_loc)
time.sleep(2)
def user_login_success(self):
return self.find_element(self.accout_id_loc).text
8 组织测试用例
· 用户名密码正确点击登录
· 用户名正确,密码错误点击登录
import unittest
from common import function,myUnit,readFile
from pageObject.loginPage import CndsPage
from time import sleep
from common.logger import Logger
from config import setting
import ddt
log = Logger()
testData= readFile.ReadFile().readYaml(setting.TEST_DATA_YAML + '/' + 'login_data.yaml')
@ddt.ddt
class LoginTest(myUnit.StartEnd):
# @unittest.skip('skip this case')
"""CNDS登录测试"""
def user_login_verify(self,account,passwd):
"""
用户登录
:param :account 账号
:param passwd: 密码
:return:
"""
CndsPage(self.driver).accout_login(account,passwd)
@ddt.data(*testData)
def test_login_normal(self,datayaml):
log.info("test_login1_normal is start run...")
self.user_login_verify(datayaml['data']['accout'],datayaml['data']['passwd'])
sleep(3)
#断言与截屏
po = CndsPage(self.driver)
if datayaml['screenshot'] == 'login_success':
po.get_account()
function.inser_img(self.driver)
self.assertEqual(po.user_login_success(), datayaml['check'][0], "登录成功,返回实际结果是->: {0}".format(po.user_login_success()))
else:
function.inser_img(self.driver)
self.assertEqual(po.accout_passwd_error(), datayaml['check'][0],"登录失败,返回实际结果是->: {0}".format(po.accout_passwd_error()))
print("test_login1_normal is test end!")
9 执行测试用例
import unittest
from common.function import latest_report
from common.sendMail import *
from config import setting
from thridLib.HTMLTestRunner import HTMLTestRunner
import time
import os,sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
report_dir = setting.TEST_REPORT + '/report/'
def add_case(test_path=setting.TEST_DIR):
discover = unittest.defaultTestLoader.discover(test_path, pattern="test*.py")
return discover
def run_case(all_case,result_path=report_dir):
print("start run testcase...")
now = time.strftime("%Y-%m-%d %H_%M_%S")
report_name = result_path + '/' + now + 'result.html'
print("start write report...")
#HTMLTestRunner测试报告
with open(report_name, 'wb') as f:
runner = HTMLTestRunner(stream=f, title='测试报告', description='用例执行情况') # 定义测试报告
runner.run(all_case) # 执行测试用例
f.close()
print("find latest report...")
# 查找最新的测试报告
report = latest_report(result_path)
# 邮件发送报告
print("send email report...")
send_mail(report)
print("test end!")
if __name__ == '__main__':
cases = add_case()
run_case(cases)
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!