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 driverexcept 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 = driverconfig = 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)@staticmethoddef 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.textexcept 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)
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!