网上现在有不少对于PageObject的说明和理解,可是从我看来,对待这种方式,每个人都有自己的理解方式,我自己其实也看了网上的一些介绍,但是说真的,有时候不是特别想去按照别人的思维方式来进行。不管别人的东西对不对。另外每个人的基础知识的储备不同,也让大家在对待一个问题的时候,理解的方向就会有所不同。那么作为个人,我的通俗非专业的理解如下:
- PO 模式 = pageobject
- PO 是一种通过不断分离数据(参数化),不断的分离出公用代码(模块化),不断的将业务和实现过程分离的一种非官方的一种方式。
- PO 是一种将page形成类和对象,统一管理的思路
针对上面的自己个人理解和实际操作,这次介绍的是通过继承的方式来设计
1. 首先介绍一下个人的代码文件夹结构和说明:
image.png
如上图:
excel_doc : 该文件夹主要是用于存放execl的测试用例excel,以及针对与excel的一个py文件,用于对test用例的操作
image.png
log: 顾名思义,就是存放执行过程中的log日志,同时log的py文件也在其中
report:很好理解的一个东西,就是测试报告,通过htmltestrunner自动生成的
testcase: 该地方就是存储执行的用例的地方。
image.png
testproject:该地方主要就是存储baseclass ,以及对应的页面类
image.png
test_all_case: 测试执行的py文件
image.png
如上就是自己的测试用例文件夹目录,这东西不是固定的,其实个人感觉你完全可以根据自己的喜好来定义这个名称和目录,比如把page类,放在一个文件夹目录也是可以的,不用纠结与网上的各种目录结构。
整体设计思路如下:
image.png
- 如何开展代码,从那里开始着手?
这个地方我要说一下,其实这个完全看个人的代码编写能力,如果是代码老员工的话,那么直接就可以从整个case的外环境编写,比如baseMethod,commonMethod,test-all-caese等,如果是新人,个人建议从testcase来写。然后不断的去参数化,模块化。
我就按照我的方式来说明一下,比较综合:
第一步: 封装自己需要到的一些基础webdriver中需要的方法
selenium自带的webdriver的什么定位,输入值等都已经够用了。但是为了让自己实际使用起来更加符合自己的想法,我们需要对其进行二次的封装和重写,当然这里说的好像很高大上,但是就我的能力来说,就是为了看起来稍微简洁一点:
举例部分BaseMethodClass类方法:
# coding=utf-8
__author__ = 'Administrator'
from selenium import webdriver
import time
import os
# import xlrd
# import xlwt
from selenium.webdriver.support.ui import WebDriverWait
# from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
class BaseMethodClass(object):
"""
基础类方法、主要是用于写用户的基础操作函数,目前来说总的是对现在webdriver默认的函数进行改写
变为更加易读的方式
"""
# 设置需要的浏览器对象
def set_browser(self, browser_name):
if browser_name == 'chrome':
self.browser = webdriver.Chrome()
elif browser_name == "ie":
self.browser = webdriver.Ie()
elif browser_name == 'firefox':
self.browser = webdriver.Firefox()
# 退出该浏览器的下的对象
def quit_browser(self):
self.browser.quit()
# 关闭浏览器
def close_browser(self):
self.browser.close()
# 打开指定的网页并且最大化
def open_web_and_maxsize(self, url):
self.browser.get(url)
self.browser.maximize_window()
# 定位一个元素
def find_element(self, type_name, location):
try:
if type_name == 'id':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_id(location))
return ele
# return self.browser.find_element_by_id(location)
elif type_name == 'css':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_css_selector(location))
return ele
# return self.browser.find_element_by_css_selector(location)
elif type_name == 'xpath':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_xpath(location))
return ele
# return self.browser.find_element_by_xpath(location)
elif type_name == 'classname':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_class_name(location))
return ele
# return self.browser.find_element_by_class_name(location)
elif type_name == 'name':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_name(location))
return ele
# return self.browser.find_element_by_name(location)
elif type_name == 'tagname':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_tag_name(location))
return ele
# return self.browser.find_element_by_tag_name(location)
elif type_name == 'part_linktext':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_partial_link_text(location))
return ele
# return self.browser.find_element_by_partial_link_text(location)
elif type_name == 'linktext':
ele = WebDriverWait(self.browser, 10).until(
lambda browser: browser.find_element_by_link_text(location))
return ele
# return self.browser.find_element_by_link_text(location)
except:
print u"没有找到该 %s 元素位置:%s" % (type_name, location)
如上代码,base类中对于定位元素和退出浏览器等方法都进行了改装
CommonMethod模块
主要是写一些,例如邮件发送,找到case,生成报告文件等
# coding=utf-8
import smtplib
__author__ = 'Administrator'
import os
from BaseMethod import BaseMethodClass
import unittest
from email.mime.text import MIMEText
from email.header import Header
import time
# 函数用到的数据
# report_path_1 = os.path.abspath(os.path.join(os.getcwd(), '..\\report'))
report_path_1 = "C:\\Python27\\workspace\\study\\JC_autotest\\qhddfxlPO\\report"
"""从file_path文件夹中,筛选出所有的case,并且存放到testsuite套件中"""
def find_all_case_1(file_path):
# 创建一个套件: 可理解为准备一个大袋子,用来装case这个苹果
test_suites = unittest.TestSuite()
# 发现了所有的case苹果,但是很乱
discover_case = unittest.defaultTestLoader.discover(file_path,
pattern='test1*.py',
top_level_dir=None)
# 将上面的未包装的苹果case统一放入到大袋子中
for test_suite in discover_case:
for test_case in test_suite:
test_suites.addTest(test_case)
# 返回这个袋子苹果
return test_suites
"""从report文件夹中读取最新的一个测试报告"""
def find_newest_report_1(report_path):
# 获得path下的所有文件
# report_path = report_path_1
lists = os.listdir(report_path)
# print 'List1:', lists
# 对当前文件夹下的所有文件进行排序
lists.sort(key=lambda fx: os.path.getctime(report_path + "\\" + fx))
# print 'List2:', lists
report = lists[-1]
# 返回当前最新的一个文件
newest_report = os.path.join(report_path, report)
return newest_report
"""不带附件的邮件"""
def send_mail_2(receiver_user, receiver_pwd):
# 发送者邮箱
sender = "wy956486535@126.com"
# 接受者邮箱
receiver = receiver_user
# 通过XXsmtp的服务器
smtpserver = 'smtp.126.com'
# 发送邮件中的主题
email_subject = u'XXXXX项目自动化测试运行报告'
# 发送邮件中的内容
email_content = find_newest_report_1(report_path_1)
# 发送邮箱需要指定人员的授权名称和密码
grant_person = receiver_user
grant_pwd = receiver_pwds
# 将或者的邮件内容转换成可读取状态
fp1 = file(email_content, 'rb')
content = fp1.read()
# msg = email.MIMEText(content, _charset='utf-8', _subtype='html')
msg = MIMEText(content, _charset="utf-8", _subtype="html")
msg["subject"] = Header(email_subject, "utf-8")
# 发送邮件
smtp = smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(grant_person, grant_pwd)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()
"""生成动态时间测试报告"""
def makeA_report_1(reportpath):
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
# reportpath1 = os.path.abspath(os.path.join(os.getcwd(), '..\\report'))
report_path = reportpath + '\\' + now + "-" + "report.html"
report_file = file(report_path, 'wb')
return report_file
第二步:通过继承Base类,对Page页面中的功能进行封装
image.png
第三步:通过继承page类,进行页面的case设计,
image.png
由于通过是不断的继承,所以在后期的代码效果就是
image.png
对于case的管理,目前也有两方方式:
image.png
每个py文件就一个case(优点是,并且每个case都是通过py文件独立,互相之间影响小 ,但是对于系统测试点详细的时候,会出现大量的py文件,不适合后期管理,个人觉得比较适合那种关于流程性的case)
image.png
一个py文件中,执行多个case,(优点是生成的py文件少,一个py中就可以实现多case,比较好管理,缺点是,这种方式如果后期需要公用一个driver的时候,就比较难理解)
第四步:case完成后,就可以对testcase的执行执行设计,如下:
# coding=utf-8
__author__ = 'Administrator'
from testproject1.CommonMethod import *
from study.JC_autotest.qhddfxlPO.log import log
import HTMLTestRunner
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# import unittest
if __name__ == "__main__":
title = u"青海电大测试报告"
description = u"青海电大测试"
file_path = "C:\\Python27\\workspace\\study\\JC_autotest\\qhddfxlPO\\testcase1"
rep = makeA_report_1("C:\\Python27\\workspace\\study\\JC_autotest\\qhddfxlPO\\report")
runner = HTMLTestRunner.HTMLTestRunner(
stream=rep,
title=title,
description=description
)
# runner = unittest.TextTestRunner()
all_testcases = find_all_case_1(file_path)
runner.run(all_testcases)
# 如果这个没有,那么就会出现在htmltestrunner执行的报告中写入的报告不全的情况
rep.close()
send_mail_2("wxxxxx@126.com", "wyxxxxx35")
上图中的,find_all_case 和 makeA_report 都是在commonmethod中的封装的方法,此处通过导入后,直接调用的
综上所属,基本上一个简单的通过继承关系得到的po就出来了。
个人感觉这个po的方便在于不管是Base类中的方法,还是page类中的方法,,都可以在你实现自己的case的业务脚本中调用,不会让你在case中,因为page未有对应的业务方法而去在page中去特意封装一个和base类一样的方法
但是也有人对这种的存在疑问: 因为case和page并不存在is a 这种子父类的关系,从某一方面来说这种设计是不合理的,但是有时候一个阶段,自己就会有一个阶段的自我认识,而当时我的思路就是这样的。
网友评论