zoukankan      html  css  js  c++  java
  • 39 web自动化 By类 登录未授权(弹框)

    目录(82、27min)

    1.By

    2.登录未授权问题

    3.等待

    正文

    1.By

    背景:上篇文章提到,在定义类属性:元素定位的时候,By方式是class name的时候,没有下划线_,下面来进行深入分析,为什么没有下划线?

    首先看下find_element()的源码:有2个参数,by给了默认值By.ID 

        def find_element(self, by=By.ID, value=None):
            """
            Find an element given a By strategy and locator. Prefer the find_element_by_* methods when
            possible.
    
            :Usage:
                element = element.find_element(By.ID, 'foo')
    
            :rtype: WebElement
            """
            if self._w3c:
                if by == By.ID:
                    by = By.CSS_SELECTOR
                    value = '[id="%s"]' % value
                elif by == By.TAG_NAME:
                    by = By.CSS_SELECTOR
                elif by == By.CLASS_NAME:
                    by = By.CSS_SELECTOR
                    value = ".%s" % value
                elif by == By.NAME:
                    by = By.CSS_SELECTOR
                    value = '[name="%s"]' % value
    
            return self._execute(Command.FIND_CHILD_ELEMENT,
                                 {"using": by, "value": value})['value']

    再点击进入查看By的源码:By的类属性名对应的值都是没有下划线的。

    class By(object):
        """
        Set of supported locator strategies.
        """
    
        ID = "id"
        XPATH = "xpath"
        LINK_TEXT = "link text"
        PARTIAL_LINK_TEXT = "partial link text"
        NAME = "name"
        TAG_NAME = "tag name"
        CLASS_NAME = "class name"
        CSS_SELECTOR = "css selector"

    那么,username_locator = {"by":"name","value":"phone"} 就等价于username_locator = {"by":By.NAME,"value":"phone"} ,

    用By的类属性的方式的好处:①编写有提示,不会出错;②编写代码,尽量用变量名表示,不写死。

    login.py优化,如下:

    """登录页面"""
    from selenium.webdriver.common.by import By
    from middware.pages.index import IndexPage
    from middware.handler import HandlerMiddle
    
    
    class LoginPage:
        """登录"""
        URL  =HandlerMiddle.yaml_data["host"] + "/Index/login.html"
        #登录按钮,元祖形式
        #login_btn_locator = ("name","btn-special")
        #登录按钮
        login_btn_locator = {"by":By.CLASS_NAME,"value":"btn-special"}
        #用户名
        username_locator = {"by":By.NAME,"value":"phone"}
        #密码
        password_locator = {"by":By.NAME,"value":"password"}
        #登陆失败的错误信息
        error_msg_locator = {"by":By.CLASS_NAME,"value":"form-error-info"}
    
    
        #初始化driver
        def __init__(self,driver):
            self.driver = driver
    
        def get(self):
            """访问页面"""
            self.driver.get(self.URL)
            return self
    
    
        def login_fail(self,username,password):
    
            # 元素定位+元素操作,输入用户名和密码,点击登录进行提交
            self.enter_username(username)
            self.enter_password(password)
            #self.driver.find_element(*self.login_btn_locator).click()
            self.driver.find_element(**self.login_btn_locator).click()#点击登录按钮
            return self
    
        def login_success(self,username,password):
    
            # 元素定位+元素操作,输入用户名和密码,点击登录进行提交
            self.enter_username(username)
            self.enter_password(password)
            self.driver.find_element(**self.login_btn_locator).click()#点击登录按钮
    
            return IndexPage(self.driver)
    
        def enter_username(self,username):
            "输入用户名"
            self.driver.find_element(**self.username_locator).send_keys(username)
            return self
    
        def enter_password(self,password):
            "输入密码"
            self.driver.find_element(**self.password_locator).send_keys(password)
            return self
    
        def get_error_info(self):
            "获取登录失败的错误信息"
            return self.driver.find_element(**self.error_msg_locator).text

    By总结:

    ①代码的可读性也进一步增强 (看到CLASS_NAME,立马知道是find_element_by_class_name的方式进行元素定位)

    ②编写过程,避免出现编写错误(调用,编译器有提示,减少错误)

    2.登录未授权-------JS暂停进行定位

    背景:在账号/密码输入错误的时候,有个弹框,弹出来几秒就消失了,无法进行元素定位,该怎么办?

    解决措施:弹框弹出的时候,暂停JS操作,让弹框暂停不动。< ---------(弹框在web中涉及到JS加载,是JS命令调出来的)

    暂停代码运行的方式:断点

    所以可以采取断点调试的方式,让弹框卡住,不让其消失。

    操作:F12进入页面调试,login.html中有JS代码,右边是调试进行的基本操作(暂停、单步执行。。。。。)

     下面操作暂停弹框,进行元素定位的步骤:

    ①F12进入source模式,输入用户名、密码,点击登录-

    ②出现弹框,点击右边【暂停】按钮,暂停JS执行

    ③对【账号或者密码错误】进入定位(查看是否定位的元素的唯一性:比如通过class属性定位的,用CSS选择器查看是否只有一个)

    这样的话,就完成了弹框的元素定位。--->通过手动定位的元素,可以记录在LoginPage中的类属性。

    #登录失败,没有授权的元素定位方式
    invalid_msg_locator = {"by":By.CLASS_NAME,"value":"layui-layer-content"}

    元素定位成功,可以进行定位方法的封装了。

    但是,直接进行定位的方法封装,会定位不到元素,因为页面加载中,弹框是后加载出来的,需要加等待时间。

    其次,点击登录以后,用class进行定位并返回text文本,有可能返回值是None,(文本还没加载出来)----这个时候隐式等待就可能找不到文本

    注意:通过隐式等待可以等待元素被加载,但是,元素被加载并不表示里面的动态文本内容能够被获取

    添加显式等待:①visible 等待元素可见

           ②text文本定位

    强制等待:把握好时间

    完善登录未授权的代码:

    ①login_data.py中添加测试用例数据

    """登录测试用例数据"""
    
    #登录失败数据
    data_error = [
        {"username":"","password":"","expected_results":"请输入手机号"},
        {"username":"123","password":"","expected_results":"请输入正确的手机号"}
    ]
    
    #登录成功用例
    data_success = [
        {"username": "15955100283", "password": "Cj900815", "expected_results": "我的帐户[小蜜蜂177872141]"}
    
    ]
    
    #登录未授权用例
    data_invalid = [
        {"username": "15955100283", "password": "15", "expected_results": "账号或密码错误!"}
    
    ]

    ②login.py模块的LoginPage类中,添加未授权的定位方法,来获取text文本,用作最后的断言

    """登录页面"""
    from selenium.webdriver.common.by import By
    from middware.pages.index import IndexPage
    from middware.handler import HandlerMiddle
    
    
    class LoginPage:
        """登录"""
        URL  =HandlerMiddle.yaml_data["host"] + "/Index/login.html"
        #登录按钮,元祖形式
        #login_btn_locator = ("name","btn-special")
        #登录按钮
        login_btn_locator = {"by":By.CLASS_NAME,"value":"btn-special"}
        #用户名
        username_locator = {"by":By.NAME,"value":"phone"}
        #密码
        password_locator = {"by":By.NAME,"value":"password"}
        #登陆失败的错误信息
        error_msg_locator = {"by":By.CLASS_NAME,"value":"form-error-info"}
        #登录失败,没有授权的元素定位方式
        invalid_msg_locator = {"by":By.CLASS_NAME,"value":"layui-layer-content"}
    
    
        #初始化driver
        def __init__(self,driver):
            self.driver = driver
    
        def get(self):
            """访问页面"""
            self.driver.get(self.URL)
            return self
    
    
        def login_fail(self,username,password):
    
            # 元素定位+元素操作,输入用户名和密码,点击登录进行提交
            self.enter_username(username)
            self.enter_password(password)
            #self.driver.find_element(*self.login_btn_locator).click()
            self.driver.find_element(**self.login_btn_locator).click()#点击登录按钮
            return self
    
        def login_success(self,username,password):
    
            # 元素定位+元素操作,输入用户名和密码,点击登录进行提交
            self.enter_username(username)
            self.enter_password(password)
            self.driver.find_element(**self.login_btn_locator).click()#点击登录按钮
    
            return IndexPage(self.driver)
    
        def enter_username(self,username):
            "输入用户名"
            self.driver.find_element(**self.username_locator).send_keys(username)
            return self
    
        def enter_password(self,password):
            "输入密码"
            self.driver.find_element(**self.password_locator).send_keys(password)
            return self
    
        def get_error_info(self):
            "获取登录失败的错误信息"
            return self.driver.find_element(**self.error_msg_locator).text
    
    
        def get_invalid_info(self):
            """登录未授权 获取未授权弹框信息"""
            return self.driver.find_element(**self.invalid_msg_locator).text

    将准备好的data,添加到parametrize,实现数据驱动

    ③test_login.py测试用例中,添加登录未授权的测试方法

    """登录功能的测试用例"""
    
    import pytest
    # from middware.handler import HandlerMiddle
    from middware.pages.login import LoginPage
    from data.login_data import data_error,data_success,data_invalid
    from middware.handler import HandlerMiddle
    #获取excel中login数据
    # data = HandlerMiddle.excel.read_data("login")
    
    
    @pytest.mark.login
    class TestLogin:
        """登录功能的测试类"""
    
        @pytest.mark.error
        @pytest.mark.parametrize("test_info",data_error)
        def test_login_error(self,test_info,driver):
           """登陆失败用例"""
    
           #初始化 操作的页面 对象
           login_page  = LoginPage(driver)
    
           #测试步骤:输入用户名、密码、登录(调用po中的方法)
           actual_result =login_page.get().login_fail(username=test_info["username"],
                            password=test_info["password"]).get_error_info()
    
           # 断言
           expected_result = test_info["expected_results"]
           try:
            assert actual_result in expected_result
           except AssertionError as e:
               HandlerMiddle.logger.error("测试用例username为{},不通过!".format(test_info["username"]))
               raise e
    
    
        @pytest.mark.success
        @pytest.mark.parametrize("test_info",data_success)
        def test_login_success(self,test_info,driver):
            """登录成功测试用例"""
    
            #初始化页面对象
            login_page = LoginPage(driver)
    
            #执行测试,获取实际结果,
            actual_result = login_page.get().login_success(username=test_info["username"],
                            password=test_info["password"]).get_account_name()
    
            #断言
            try:
                assert actual_result in test_info["expected_results"]
            except AssertionError as e:
                HandlerMiddle.logger.error("测试用例username为{},不通过!".format(test_info["username"]))
                raise e
    
        @pytest.mark.invalid
        @pytest.mark.parametrize("test_info",data_invalid)
        def test_login_invalid(self,test_info,driver):
            """登录未授权的测试用例"""
    
            #初始化页面
            login_page = LoginPage(driver)
            #执行测试用例 获取测试结果
            actual_result = login_page.get().login_fail(username=test_info["username"],
                            password=test_info["password"]).get_invalid_info()
    
            #断言
            try:
                assert actual_result in test_info["expected_results"]
            except AssertionError as e:
                HandlerMiddle.logger.error("测试用例username为{},不通过!".format(test_info["username"]))
                raise e

    用例筛选时,记得去pytest.ini中注册标签invalid

    总结:web自动化测试用例实现的流程(比如test_login.py测试用例的编写)

    ①准备前置、后置条件 conftest 中的测试夹具fixture

    ②编写测试步骤:测试用例的函数注解 docstring

       例如:

    登录失败的docstring

          1)登录页面输入用户名

       2)登录页面输入密码

          3)登录页面点击登录按钮

          4)登录页面获取登录失败的错误信息

    登录成功的docstring

          1)登录页面输入用户名

       2)登录页面输入密码

          3)登录页面点击登录按钮

          4)首页页面获取登录用户账号信息(进行了页面跳转)

    在封装的方法中,添加docstring的好处,按照步骤进行封装的方法的编写,减少页面跳转的错误。(前提:docstring要写对啊)

    ----手工进行测试步骤的验证,确保测试步骤能够实现哪些功能(手工测试的时候,可以直接将需要进行元素定位的类属性完成,写好)

    ③根据测试步骤,封装页面行为

    ④调用页面行为,获取实际结果

    ⑤断言

    83节

    根据上面的步骤,进行get_invalid_info() 进行显示等待的元素定位:

        def get_invalid_info(self):
            """登录未授权 获取未授权弹框信息"""
            # return self.driver.find_element(**self.invalid_msg_locator).text
            elem = WebDriverWait(self.driver,timeout=20,poll_frequency=0.5).until(
                expected_conditions.visibility_of_element_located(self.invalid_msg_locator.values()))
            return elem.text

    上面的visibility_of_element_located()的参数是一个元祖,在类属性定义的时候,需要改为元祖。

    不修改为元祖的话,也可以将字典.values()转化为列表,也是OK的。(Python是动态语言,对格式的要求不会那么严格,了解即可)

    在项目中,显示等待可能会经常使用,那么将显式等待封装起来~~~~~~~~~~~~~~~~~~

     封装:等待元素可见的方法

        def get_invalid_info(self):
            """登录未授权 获取未授权弹框信息"""
       
            elem = self.wait_element_visible(self.invalid_msg_locator.values())
            return elem.text
    
        def wait_element_visible(self,locator,timeout = 20,poll = 0.5):
            """显式等待元素可见
            :return elem
            """
            ele = WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(
                expected_conditions.visibility_of_element_located(locator)
            )
            return ele

    在定义LoginPage中,元素定位方式的类属性时,用的是字典,在实际项目中,用元祖的方式更加简单,不需要类型转化,

    其次是,在进行显性等待的时候,

    ele = WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(
                expected_conditions.visibility_of_element_located(locator) 

    中传入的locator是个元祖,直接定义类属性为元祖,直接调用即可。

    下面将login.py中类属性的字典表示方式改为元组形式。
    """登录页面"""
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions
    from selenium.webdriver.support.wait import WebDriverWait
    
    from middware.pages.index import IndexPage
    from middware.handler import HandlerMiddle
    
    
    class LoginPage:
        """登录"""
        URL  =HandlerMiddle.yaml_data["host"] + "/Index/login.html"
        #登录按钮,元祖形式
        #login_btn_locator = ("name","btn-special")
        #登录按钮
        login_btn_locator = (By.CLASS_NAME,"btn-special")
        #用户名
        username_locator = (By.NAME,"phone")
        #密码
        password_locator = (By.NAME,"password")
        #登陆失败的错误信息
        error_msg_locator = (By.CLASS_NAME,"form-error-info")
        #登录失败,没有授权的元素定位方式
        invalid_msg_locator = (By.CLASS_NAME,"layui-layer-content")
    
        #初始化driver
        def __init__(self,driver):
            self.driver = driver
    
        def get(self):
            """访问页面"""
            self.driver.get(self.URL)
            return self
    
    
        def login_fail(self,username,password):
    
            # 元素定位+元素操作,输入用户名和密码,点击登录进行提交
            self.enter_username(username)
            self.enter_password(password)
            #self.driver.find_element(*self.login_btn_locator).click()
            self.driver.find_element(*self.login_btn_locator).click()#点击登录按钮
            return self
    
        def login_success(self,username,password):
    
            # 元素定位+元素操作,输入用户名和密码,点击登录进行提交
            self.enter_username(username)
            self.enter_password(password)
            self.driver.find_element(*self.login_btn_locator).click()#点击登录按钮
    
            return IndexPage(self.driver)
    
        def enter_username(self,username):
            "输入用户名"
            self.driver.find_element(*self.username_locator).send_keys(username)
            return self
    
        def enter_password(self,password):
            "输入密码"
            self.driver.find_element(*self.password_locator).send_keys(password)
            return self
    
        def get_error_info(self):
            "获取登录失败的错误信息"
            return self.driver.find_element(*self.error_msg_locator).text
    
    
        def get_invalid_info(self):
            """登录未授权 获取未授权弹框信息"""
            
            elem = self.wait_element_visible(self.invalid_msg_locator)
            return elem.text
    
        def wait_element_visible(self,locator,timeout = 20,poll = 0.5):
            """显式等待元素可见
            :return elem
            """
            ele = WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(
                expected_conditions.visibility_of_element_located(locator)
            )
            return ele

    下面将index.py文件进行locator分层封装,优化:

    """登录成功页面"""
    from selenium.webdriver.common.by import By
    from middware.handler import HandlerMiddle
    
    
    class IndexPage:
        """登录成功"""
    
        #首页的URL
        URL  =HandlerMiddle.yaml_data["host"] + "/Index.html"
    
        #用户账号locator
        user_accout_locator = (By.XPATH,'//a[@href="/Member/index.html"]')
    
        #初始化driver
        def __init__(self,driver):
            self.driver = driver
    
        #打开首页
        def get(self):
            self.driver.get(self.URL)
            return self
    
        #获取登录成功的用户名
        def get_account_name(self):
            web_element=self.driver.find_element(*self.user_accout_locator)
            return web_element.text
  • 相关阅读:
    C标准中的一些预定义宏
    将智能设备连接到开发计算机上
    ATX电源短接哪两个引脚可以开机
    Visual C++ 编译器选项
    Visual C++ 链接器选项
    vs 2005 中的单元测试的生命周期
    .net study link
    C#的多线程机制探索(http://diy8.blog.sohu.com/741499.html)
    ![网页设计与编程] (小心眼花!)(http://www.fwcn.com/Bbs/viewthread.php?tid=10521)
    多个线程可能会试图同时访问某个对象。在多个线程同时争相访问某个对象的同时,如果一个线程修改了资源,有些线程可能会收到无效状态。例如,如果某个线程读取对象的字段,同时另一线程正在修改该字段,则第一个线程可能会收到无效的字段状态。这种情况称为竞用情况。
  • 原文地址:https://www.cnblogs.com/ananmy/p/13589106.html
Copyright © 2011-2022 走看看