前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3种方式优化Python自动化代码

3种方式优化Python自动化代码

作者头像
清菡
发布2020-12-02 15:25:27
8690
发布2020-12-02 15:25:27
举报
文章被收录于专栏:清菡软件测试清菡软件测试

接上篇文章~

一、开始

1.先把要做的事一步一步用注释写出来,然后再写代码。切记一定要写注释!不然回头看不懂自己写的是啥,这就尴尬了。

2.提高测试用例运行效率,减少测试用例运行时间:

Web自动化用例在编写的时候要注意用例的独立性。当然,流程性质的用例一定是关联在一起的,而且比较长比较复杂,上下用例之间是有关联的,那就必须关联起来。

目前,每个用例都有去打开浏览器,访问网址,然后登陆,tearDown()的时候关闭。

代码语言:javascript
复制
   def setUp(self):
        # 前置  访问登陆页面
        self.driver=webdriver.Chrome()
        self.driver.get(CD.web_login_url)
        self.lg=LoginPage(self.driver)

    def tearDown(self):
        #后置
        self.driver.quit()

3.能不能在所有用例执行之前只打开一次浏览器,在所有用例执行之后,关闭浏览器呢?

可以,但是必须考虑中间某一个用例失败了是否会影响下一个用例的运行?必须考虑好这样情况发生后,任何其它用例都不会受到影响。

想做到所有用例执行之前只访问网页一次,所有用例执行完成以后只关闭一次,就必须符合以下条件:

实际上,每个测试用例的起点都是在登陆页面。

1.保证所有用例在运行的时候,起点是在登陆页面;

2.前提是当前尚未登陆成功的状态;

因为异常用例都是在登陆页面,没用登陆成功的,先执行异常用例再执行正常用例,就做到了所有用例都是尚未登陆的状态。

4.但是,你的用户名和密码已经有数据输入了,怎么才能不影响下一条用例的运行?

要做到第一次访问登陆页面一样的效果。

1.所有用例运行之前,打开浏览器,访问登陆页面;

2.每一个页面操作完成之后,操作当前页面;

3.最后一个用例是登陆成功的用例。

所有用例运行之前,打开浏览器,访问登陆页面。setUp()和tearDown()做不到,它设计的目的就是每一个用例都会去执行的。

setUpClass()是每一个测试类运行的一次setUp。这个测试类下面有好几个测试用例,但是一个测试类只运行一次。setUp代表测试用例之前运行的。一个测试类当中,所有测试用例运行之前,会先执行setUpClass(),执行完之后,再去执行测试用例。

也就是说,首先执行setUpClass里面的代码-test1-test2-test3-testN-所有用例执行完之后是tearDownClass,这个时候,测试用例变成了夹心饼干。

tearDown()是每个用例做完之后可以做的事情。

不是必須setUp()和tearDown()成对出现的。可以只用tearDown()不用setUp()。这个是需要谁就用谁。测试用例中有什么样的前置就用setUp(),如果没有就不用。setUpClass()和tearDownClass()也是一样的,需要哪个就用哪个,不需要就不用,都要用就都写。

是TestCase中同样的一个方法。点击O可看到源码。

需要控制执行顺序。

代码语言:javascript
复制
import  unittest
from selenium import webdriver
from PageObjects.login_page import LoginPage
from PageObjects.index_page import IndexPage
from TestDatas import Common_Datas as CD
from TestDatas import login_datas as LD
import ddt

@ddt.ddt

class TestLogin(unittest.TestCase):

    @classmethod

    def setUpClass(cls):
        #通过excel读取本功能当中需要的所有测试数据
        print("===所有测试用例之前的,setup====整个测试用例只执行一次========")
        cls.driver = webdriver.Chrome()
        cls.driver.get(CD.web_login_url)
        cls.lg = LoginPage(cls.driver)
        pass

    @classmethod
    def tearDownClass(cls):
        print("===所有测试用例之后的,teardown====整个测试用例只执行一次========")
        cls.driver.quit()


    # def setUp(self):
    #     # 前置  访问登陆页面
    #     pass


    def tearDown(self):
        #后置
        #每一个页面操作完成之后,要刷新当前页面
        self.driver.refresh()



    # 正常用例-登陆成功
    def test_login_1_success(self):
        #步骤  输入用户名:XXx 密码XXX 点击登陆
        self.lg.login(LD.success_data["user"],LD.success_data["passwd"])
        #断言  首页当中-能否找到  退出  这个元素
        self.assertTrue(IndexPage(self.driver).isExist_logout_ele())



    #
    #异常用例 -手机号格式不正确(大于11位、小于11位、为空、不在号码段)  ddt\
    @ddt.data(*LD.phone_data)
    def test_login_0_user_wrongFormat(self,data):
        # 步骤  输入用户名:XXx 密码XXX 点击登陆
        self.lg.login(data["user"],data["passwd"])
        # 断言  登陆页面 提示:请输入正确的手机号
        #登录页面中 -获取提示框的文本内容
        #比对文本内容与期望的值是否相等
        self.assertEqual(self.lg.get_errorMsg_from_loginArea(),data["check"])

    # @ddt.data()
    # def test_login_wrongPwd_noReg(self):
    #
    #     # 步骤  输入用户名:XXx 密码XXX 点击登陆
    #     # 断言  登陆页面 页面正中间提示:XXX
    #     # 登录页面中 -获取提示框的文本内容
    #     # 比对文本内容与期望的值是否相等
    #     pass

    # #异常用例 - 用户名为空
    # def test_login_noUser(self):
    #     self.lg.login('', 'python')
    # # 步骤  输入用户名:XXx 密码XXX 点击登陆
    # # 断言  登陆页面 提示:请输入手机号
    #     pass

#异常用例-未注册手机号
#异常用例-错误的密码
#异常用例-不输入密码

能实现这种方式有2个条件,首先必须考虑:1.每一个测试的失败,会不会影响其它用例的执行。2.如果你发现,无论如何这个问题都不好解决,或者说能解决很麻烦,就没有必要来做这种模式。

大家可以考虑下能不能实现,实现是最好的,实现不了就按照最开始讲的setUp()和tearDown(),多写点冗余的,时间多浪费点没关系。毕竟自动化代码是晚上运行的,稳定性为首要条件。

虽然做到了3次分层,但是很多网上的框架,看到别人写的框架中会有一个有意思的地方,元素定位目前是直接放在函数当中的,包括错误信息的获取,元素定位,全部放在函数当中的。

这里有个不好的地方,和测试数据的提取的方式是一样的原因:1.元素定位未必只在一个函数中用一次,有些元素定位可能在多个函数中都要用得到。2.这个页面其实不复杂。未来实际工作中不可能只有登录功能,还有其它的功能。那这个页面是比较复杂的,元素定位在几十个是很正常的。

几十个元素定位,你确认都是分布在不同的函数当中吗?

想把它分离开来就是希望能够针对性地去修改。login_page里面有元素变了,或者有操作步骤变了,那我也不想在每个函数中找元素定位方式。

二、3种方式

第一种方式,做成类的属性

记得加注释

这样写,如果只是元素定位发生变化,都不需要看下面的函数。

代码语言:javascript
复制

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By


class LoginPage:
    #元素定位
    #用户名输入框
    name_text = '//input[@name="phone"]'
    #密码输入框
    pwd_text = '//input[@name="password"]'
    #登录按钮
    login_but = '//button[text()="登录"]'
    #错误提示框-登录区域
    errorMsg_from_loginArea= '//div[@class="form-error-info"]'



    def __init__(self,driver):
        self.driver=driver

    #登陆操作
    def login(self,username,passwd,remember_user=True):
        #输入用户名
        #输入密码


        WebDriverWait(self.driver,20).until(EC.visibility_of_element_located((By.XPATH,name_text)))
        self.driver.find_element_by_xpath(self.name_text).send_keys(username)
        self.driver.find_element_by_xpath(self.pwd_text).send_keys(passwd)
       #判断一下rember_user的值,来决定是否勾选
        self.driver.find_element_by_xpath(self.login_but).click()

    #注册入口
    def register_enter(self):
        WebDriverWait(self.driver,20).until(EC.visibility_of_element_located((By.XPATH,"")))
        self.driver.find_element_by_xpath("").click()

    #获取错误提示信息-登录区域
    def get_errorMsg_from_loginArea(self):
        WebDriverWait(self.driver,20).until(EC.visibility_of_all_elements_located((By.XPATH,'//div[@class="form-error-info"]')))
        return self.driver.find_element_by_xpath('//div[@class="form-error-info"]').text

    #获取错误信息-页面正中间
    def get_errorMsg_from_pageCenter(self):
        #//div[@class="layui-layer-content"]
        pass

    #忘记密码

这是第一种方式,这种方式的弊端是:现在很多元素定位是Xpath,未来做项目还会用到id、css。比如现在是xpath定位,万一哪天元素多了个id,将来哪天想优化下,可能会修改定位方式。

这个地方只写了表达式没写定位类型,对应到这里的方法就是find_element_by_xpath()这里的函数名称是要跟元素定位表达式和定位类型保持完全一致的。 改的时候比较痛苦。

第二种方式,把元素定位类型和元素定位表达式全部都写在一起。

如果元素定位方式发生改变,下面的查找元素不受影响。改成什么类型,就用什么类型。find_element()自动会去用的。

find_element()源码里有对各种方式的判断:

元素定位和元素操作互不影响。

第三种方式,把元素定位和函数的操作分开。

参考By的源码,这个类中只定义了数据,没有方法:

在PageLocators中,跟页面一一对应。

loc.后面接的都是元素定位表达式,看名字筛选就好了。 如果是继承self.会有一些函数名称跟它有很高的重复度,self.的时候,要点的东西就很多了。

选的东西有点多,也有些是内置的driver,也不记得每个元素定位是什么样的,就有点混乱。

locator是元素定位的总称。

单向调用。PageObjects调用了PageLocators。

TestCases调用了TestDatas和PageObjects。

写代码的时候特别注意不要出现双向调用。

三、代码

loginpage_locators.py

代码语言:javascript
复制

from selenium.webdriver.common.by import By


class LoginPageLocator:
    #元素定位
    #用户名输入框
    name_text =(By.XPATH,'//input[@name="phone"]')
    #密码输入框
    pwd_text = (By.XPATH,'//input[@name="password"]')
    #登录按钮
    login_but =(By.XPATH,'//button[text()="登录"]')
    #错误提示框-登录区域
    errorMsg_from_loginArea= '//div[@class="form-error-info"]'

index_page.py

代码语言:javascript
复制
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

class IndexPage:

    def __init__(self,driver):
        self.driver=driver

    def isExist_logout_ele(self):
        # 等待10秒 元素有没有出现 //a[@href="/Index/logout.html"]
        #如果存在就返回True,不存在就返回False
        try:
            WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//a[@href="/Index/logout.html"]')))
            return True
        except:
            return False

login_page.py

代码语言:javascript
复制

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from PageLocators.loginpage_locators import LoginPageLocator as loc

class LoginPage:
    def __init__(self,driver):
        self.driver=driver

    #登陆操作
    def login(self,username,passwd,remember_user=True):
        #输入用户名
        #输入密码
        WebDriverWait(self.driver,20).until(EC.visibility_of_element_located((self.name_text)))
        self.driver.find_element(*loc.name_text).send_keys(username)
        self.driver.find_element(*loc.pwd_text).send_keys(passwd)
       #判断一下rember_user的值,来决定是否勾选
        self.driver.find_element(*loc.login_but).click()

    #注册入口
    def register_enter(self):
        WebDriverWait(self.driver,20).until(EC.visibility_of_element_located((By.XPATH,"")))
        self.driver.find_element_by_xpath("").click()

    #获取错误提示信息-登录区域
    def get_errorMsg_from_loginArea(self):
        WebDriverWait(self.driver,20).until(EC.visibility_of_all_elements_located((By.XPATH,'//div[@class="form-error-info"]')))
        return self.driver.find_element_by_xpath('//div[@class="form-error-info"]').text

    #获取错误信息-页面正中间
    def get_errorMsg_from_pageCenter(self):
        #//div[@class="layui-layer-content"]
        pass

    #忘记密码

test_login.py

代码语言:javascript
复制
import  unittest
from selenium import webdriver
from PageObjects.login_page import LoginPage
from PageObjects.index_page import IndexPage
from TestDatas import Common_Datas as CD
from TestDatas import login_datas as LD
import ddt

@ddt.ddt

class TestLogin(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        #通过excel读取本功能当中需要的所有测试数据
        print("===所有测试用例之前的,setup====整个测试用例只执行一次========")
        cls.driver = webdriver.Chrome()
        cls.driver.get(CD.web_login_url)
        cls.lg = LoginPage(cls.driver)
        pass

    @classmethod
    def tearDownClass(cls):
        print("===所有测试用例之后的,teardown====整个测试用例只执行一次========")
        cls.driver.quit()


    # def setUp(self):
    #     # 前置  访问登陆页面
    #     pass


    def tearDown(self):
        #后置
        #每一个页面操作完成之后,要刷新当前页面
        self.driver.refresh()



    # 正常用例-登陆成功
    def test_login_1_success(self):
        #步骤  输入用户名:XXx 密码XXX 点击登陆
        self.lg.login(LD.success_data["user"],LD.success_data["passwd"])
        #断言  首页当中-能否找到  退出  这个元素
        self.assertTrue(IndexPage(self.driver).isExist_logout_ele())



    #
    #异常用例 -手机号格式不正确(大于11位、小于11位、为空、不在号码段)  ddt\
    @ddt.data(*LD.phone_data)
    def test_login_0_user_wrongFormat(self,data):
        # 步骤  输入用户名:XXx 密码XXX 点击登陆
        self.lg.login(data["user"],data["passwd"])
        # 断言  登陆页面 提示:请输入正确的手机号
        #登录页面中 -获取提示框的文本内容
        #比对文本内容与期望的值是否相等
        self.assertEqual(self.lg.get_errorMsg_from_loginArea(),data["check"])

    # @ddt.data()
    # def test_login_wrongPwd_noReg(self):
    #
    #     # 步骤  输入用户名:XXx 密码XXX 点击登陆
    #     # 断言  登陆页面 页面正中间提示:XXX
    #     # 登录页面中 -获取提示框的文本内容
    #     # 比对文本内容与期望的值是否相等
    #     pass

    # #异常用例 - 用户名为空
    # def test_login_noUser(self):
    #     self.lg.login('', 'python')
    # # 步骤  输入用户名:XXx 密码XXX 点击登陆
    # # 断言  登陆页面 提示:请输入手机号
    #     pass

#异常用例-未注册手机号
#异常用例-错误的密码
#异常用例-不输入密码

Common_Datas.py

代码语言:javascript
复制

#全局-系统访问地址-登录链接

web_login_url="http://120.78.128.25:8765/Index/login.html"

login_datas.py

代码语言:javascript
复制
#正常场景-测试数据
success_data={"user":"18684720553","passwd":"python"}

#异常用例-手机号格式不正确(大于11位、小于11位、为空、不在号码段)
phone_data=[
{"user":"18684720","passwd":"python","check":"请输入正确的手机号"},
{"user":"18684720553123","passwd":"python","check":"请输入正确的手机号"},
{"user":"","passwd":"python","check":"请输入手机号"},
{"user":"11684720553","passwd":"python","check":"请输入正确的手机号"}
]

#异常用例

# layui-layer-content 此账号没有经过授权,请联系管理员! //div[@class="layui-layer-content"]

此代码未运行,代码未写完,未完待续~

代码截图:

四、总结代码优化了3点

1.数据分离-TestDatas

为什么要做数据分离?

1)多环境切换。

2)数据公用。

3)好维护。如果有多个环境,我可以统一修改。

如果有公共数据,我就准备一份就好啦。无论是模块级别的公共数据还是整个测试系统的公共数据,降低重复度,方便管理。

2.测试用例-引用ddt

降低了用例的重复度。

3.优化了执行效率:

setUpClass

tearDownClass

之前是每条用例都要打开页面,关闭页面。现在是执行全部用例前打开一次,执行全部用例后关闭。但是要保证每条用例间互不影响。

4.元素定位和函数分离:元素定位类型和表达式用元组来管理-PageLocators层。

五、问题总结

1.写自动化代码的顺序

先把页面封装起来,页面封装起来的时候必须依赖于测试用例的分析和业务功能的分析。实际过程中,不会先写用例,会先把页面封装。页面封装完成之后,再去写测试用例。

都已经准备好了,用例里面直接调用就行了。

在页面封装的过程中,元素定位和页面功能是一起实现的。先把元素定位准备好,再去写页面功能。

如果哪些元素定位是当时没定位好的,再去补就好了。这种模式下,在哪个页面补都是可以的,不影响其它部分。没有的都可以在上面追加,每一块都是可以这样做的。都不影响已写好的部分,也不需大改。

2.注意

在不清楚页面封装的情况下,最好的方式是:把测试用例用注释的方式写出来(不需要写代码),然后再一步一步补上代码。

3.Python框架和Python自动化框架有什么区别?

都是框架,方向不同。Python框架包含unittest

Python自动化框架目的非常明确是做项目级别的自动化测试的。

4.做自动化要执行那么多异常用例吗?

先执行正常的用例,如果是非常简单的异常用例就写。看情况,时间上安排得过来再去写异常的用例。

5.三次错误密码,会有验证码,这块怎么处理?

绕过验证码,3次错误密码,再写个用例对密码重试。

3次错误密码这个做不做自动化,看情况。

6.短信验证码去数据库查。

7.回归用例要不要有异常用例,因人因公司因项目而异。


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 清菡软件测试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、开始
    • 1.先把要做的事一步一步用注释写出来,然后再写代码。切记一定要写注释!不然回头看不懂自己写的是啥,这就尴尬了。
      • 2.提高测试用例运行效率,减少测试用例运行时间:
        • 3.能不能在所有用例执行之前只打开一次浏览器,在所有用例执行之后,关闭浏览器呢?
          • 想做到所有用例执行之前只访问网页一次,所有用例执行完成以后只关闭一次,就必须符合以下条件:
        • 4.但是,你的用户名和密码已经有数据输入了,怎么才能不影响下一条用例的运行?
        • 二、3种方式
          • 第一种方式,做成类的属性
            • 第二种方式,把元素定位类型和元素定位表达式全部都写在一起。
              • 第三种方式,把元素定位和函数的操作分开。
                • 1.数据分离-TestDatas
                • 2.测试用例-引用ddt
                • 3.优化了执行效率:
                • 4.元素定位和函数分离:元素定位类型和表达式用元组来管理-PageLocators层。
            • 三、代码
            • 四、总结代码优化了3点
            • 五、问题总结
              • 1.写自动化代码的顺序
              • 2.注意
                • 3.Python框架和Python自动化框架有什么区别?
                  • 4.做自动化要执行那么多异常用例吗?
                    • 5.三次错误密码,会有验证码,这块怎么处理?
                      • 6.短信验证码去数据库查。
                        • 7.回归用例要不要有异常用例,因人因公司因项目而异。
                        相关产品与服务
                        验证码
                        腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档