美文网首页WebUI自动化
关于自动化Python+Selenium的自动化测试实践——之P

关于自动化Python+Selenium的自动化测试实践——之P

作者: 金鱼座 | 来源:发表于2019-12-13 14:44 被阅读0次

    网上现在有不少对于PageObject的说明和理解,可是从我看来,对待这种方式,每个人都有自己的理解方式,我自己其实也看了网上的一些介绍,但是说真的,有时候不是特别想去按照别人的思维方式来进行。不管别人的东西对不对。另外每个人的基础知识的储备不同,也让大家在对待一个问题的时候,理解的方向就会有所不同。那么作为个人,我的通俗非专业的理解如下:

    1. PO 模式 = pageobject
    2. PO 是一种通过不断分离数据(参数化),不断的分离出公用代码(模块化),不断的将业务和实现过程分离的一种非官方的一种方式。
    3. 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
    1. 如何开展代码,从那里开始着手?
      这个地方我要说一下,其实这个完全看个人的代码编写能力,如果是代码老员工的话,那么直接就可以从整个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 这种子父类的关系,从某一方面来说这种设计是不合理的,但是有时候一个阶段,自己就会有一个阶段的自我认识,而当时我的思路就是这样的。

    相关文章

      网友评论

        本文标题:关于自动化Python+Selenium的自动化测试实践——之P

        本文链接:https://www.haomeiwen.com/subject/orpfnctx.html