zoukankan      html  css  js  c++  java
  • 37 web自动化实战三 前置后置条件 (fixture yield知识点 conftest.py )

    目录

    80节

    1.前置后置条件----测试夹具fixture

    2.conftest.py 文件

    3.重运行机制 

    1.测试夹具fixture

    背景:之前的测试代码中,并没有实现测试完成后,关闭浏览器操作的行为。

    这么多年测试经验的你,不难理解:打开浏览器、关闭浏览器,可以看做是前置条件和后置条件。

    那么在unittest中的setUp() 、tearDown()在这里还能不能用呢?答案是否定的,因为pytest的类在使用fixture是不兼容unittest的,不能继承unittest.TestCase,就无法使用setUp() 、tearDown()。(setUp() 、tearDown()是unittest框架里的功能啦)

    智能的pytest,提供了一套系统,作为前置、后置条件---------->测试夹具

    申明测试夹具实现语法:

    ①定义的普通函数(浏览器函数(这里定义函数名是driver))上,添加@pytest.fixture(),申明这是个测试夹具

    ②把return driver改为 :yield driver ,实现前置、后置条件(yield前面的就是前置条件,后面的就是后置条件)

       yield可以理解为return,有返回值------区别是,执行yield,其后面的代码会继续执行,执行return,其后面的代码不会执行

    ③后置清理语句:driver.quit()

    测试夹具如何使用:

    将测试夹具的定义的函数名,作为参数,放到测试用例的函数中------>def test_login_error(self,test_info,driver):

    程序运行顺序:

    测试用例的方法中要用到driver,调用driver这个测试夹具,再执行测试用例的方法,最后再执行后置清理)

    代码实现如下: 

    """登录功能的测试用例"""
    
    import pytest
    from middware.handler import HandlerMiddle
    from config.config import Wait_time
    
    data = HandlerMiddle.excel.read_data("login")
    
    @pytest.fixture()
    def driver():
        from selenium import webdriver
    
        ##前置条件
        # 1.打开浏览器
        driver = webdriver.Chrome()
        # 设置隐性等待 等待的时间就可以放在config中,直接参数调用
        ##方法一:放在yaml中
        wait_time = HandlerMiddle.yaml_data["selenium"]["wait_time"]
        ##方法二、放在config.py中
        # wait_time = Wait_time
        driver.implicitly_wait(wait_time)
    
        yield driver
    
        #后置q清理
        driver.quit()
    
    
    @pytest.mark.login
    class TestLogin():
        """登录功能的测试类"""
        @pytest.mark.smoke
        @pytest.mark.error
        @pytest.mark.parametrize("test_info",data)
        def test_login_error(self,test_info,driver):
      。
      。
      。

    知识拓展:yield

    1).生成器generator的概念

     在Python中,一边循环一边计算的机制,称为生成器:generator。

    2).为什么要用生成器? 

    列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。

    如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

    如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。

    简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器!

    3).怎么创建生成器?-------2种方法

    ①只要把一个列表生成式的[]改成(),就创建了一个generator

    >>> My_list = [x * x for x in range(10)]
    >>> My_list
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]    #返回的是一个存放数据列表,数据量大,会很占内存
    >>> gen = (x * x for x in range(10))
    >>> gen   #返回一个生成器,需要哪些数据,在循环的过程中不断推算出后续的元素,节省空间
    <generator object <genexpr> at 0x1022ef630>

     L是一个My_list,而gen是一个generator

    带有 yield 关键字的函数,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象。

     举例,yield实现斐波那契数列

    def fab(max): 
        n, a, b = 0, 0, 1 
        while n < max: 
            yield b      # 使用 yield
            a, b = b, a + b 
            n = n + 1
     
    for n in fab(5): 
        print n

    如果这里的yield用 print 打印数字,会导致该函数可复用性较差,因为 fab 函数返回 None,其他函数无法获得该函数生成的数列。

    调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。

    “圆规”正传,回到fixture~~~~~~~~~~~

     在unittest中,有setUpClass,tearDownClass,那么在pytest中的fixture应该如何实现呢??

    在@pytest.fixture()中添加参数scope=“class”,在运行程序时,每个测试用例的类,只会执行一次。

    ※:pytest运行程序时,捕获所有的输出,加-s (不加-s的话,输出信息不会显示)

      即pytest -m  "tagname" -s

    举个栗子:

    不加scope=“class”(默认scope="function")

    import pytest
    #申明测试夹具
    @pytest.fixture()
    def fixt():
        #前置条件
        print("这是前置条件")
    
        yield
    
        #后置清理
        print("这是后置条件")
    
    @pytest.mark.demo
    class TestDemo():
        """测试用例类"""
    
        def test_demo1(self,fixt):
            pass
    
        def test_demo2(self,fixt):
            pass

    结果:两个用例,执行了2次

    加scope=“class”

    import pytest
    #申明测试夹具
    @pytest.fixture(scope="class")
    def fixt():
        #前置条件
        print("这是前置条件")
    
        yield
    
        #后置清理
        print("这是后置条件")
    
    @pytest.mark.demo
    class TestDemo():
        """测试用例类"""
    
        def test_demo1(self,fixt):
            pass
    
        def test_demo2(self,fixt):
            pass

    结果:一个测试类,只会执行一次测试夹具

    除了上面2种形式,还有scope=“module”类型的

    即,每个模块里面,即使有多个class,也只会执行一次前置、后置条件。

    代码实现如下:

    import pytest
    #申明测试夹具 @pytest.fixture(scope
    ="function") def function_fixt(): #前置条件 print("这是function级别前置条件") yield #后置清理 print("这是function级别后置条件") #申明测试夹具 @pytest.fixture(scope="class") def class_fixt(): #前置条件 print("这是class级别前置条件") yield #后置清理 print("这是class级别后置条件")
    #申明测试夹具 @pytest.fixture(scope
    ="module") def module_fixt(): #前置条件 print("这是module级别前置条件") yield #后置清理 print("这是module级别后置条件") @pytest.mark.demo class TestDemo(): """测试用例类""" def test_demo1(self,function_fixt,class_fixt,module_fixt): pass def test_demo2(self,function_fixt,class_fixt,module_fixt): pass @pytest.mark.demo class TestDemo2(): def test_demo3(self,function_fixt,class_fixt,module_fixt): pass

    运行pytest -m "demo" -s,结果如下:

    在实际的测试中,一个class测试类,添加一个class级别的测试夹具;不需要每个用例都打开、关闭一次浏览器,这样会降低测试效率。

    fixture的基本内容,到这里全部描述完了。

    下面再考虑个问题,在实际的测试工作中,注册、登录,取现等操作,每执行一次,就要打开/关闭一次浏览器,会比较麻烦,所以应该将fixture放在一个公共的文件中,让别的模块去调用即可。---conftest.py

    2.conftest.py 文件

    在项目路径下,直接新建一个py文件,命名为conftest.py。------注意:这个名字是固定的,作用:存储所有的fixture

    ①为什么不将测试夹具放在common模块中去?

      在因为driver这个对象,在测试用例中会经常的使用,放在common中要经常进行模块的import操作;而conftest是pytest里面有一个比较智能的地方,不需要去导入,运行程序的时候,会直接去conftest.py文件去找driver,找到了就继续运行,找不到就报错。省了很多导入的操作,也避免了因为导入模块可能存在路径错误的隐患。

    代码实现:

    """固定文件名conftest.py,存储所有的测试夹具fixture"""
    import pytest
    from middware import handler
    
    @pytest.fixture()
    def driver():
        from selenium import webdriver
    
        ##前置条件
        #打开浏览器
        driver = webdriver.Chrome()
    
        # 设置隐性等待 等待的时间就可以放在config中,直接参数调用
        wait_time = handler.HandlerMiddle.yaml_data["selenium"]["wait_time"]
        driver.implicitly_wait(wait_time)
    
        yield driver
    
        #后置清理
        driver.quit()

    test_login.py用例(无需导入conftest.py):

    """登录功能的测试用例"""
    
    import pytest
    from middware.handler import HandlerMiddle
    
    data = HandlerMiddle.excel.read_data("login")
    
    @pytest.mark.login
    class TestLogin():
        """登录功能的测试类"""

    @pytest.mark.smoke @pytest.mark.error @pytest.mark.parametrize("test_info",data) def test_login_error(self,test_info,driver): """登录失败测试步骤 1.打开浏览器 2.访问登录页面 3.元素定位+元素操作,输入用户名和密码,点击登录 4.通过获取页面内容得到实际结果,进行断言 :return: """ #2.访问登录页面 url = "http://120.78.128.25:8765/Index/login.html" driver.get(url) #3.元素定位+元素操作,输入用户名和密码,点击登录 driver.find_element_by_name("phone").send_keys(eval(test_info["data"])["username"]) #定位输入手机号为空 driver.find_element_by_name("password").send_keys(eval(test_info["data"])["password"])#定位输入的密码为空 driver.find_element_by_class_name("btn-special").click() #4.通过获取页面内容得到实际结果,进行断言 #实际结果是在页面上的提示,再次进行定位 actual_result = driver.find_element_by_class_name("form-error-info").text expected_result = test_info["expected_result"] #断言 assert actual_result == expected_result

    运行run.py(同上面的run.py)即可完成测试login_error的用例。

    3.重运行机制   -----多次运行

    背景:为什么使用重运行机制?

        对于web自动化测试中,可能因为网速的问题导致测试不通过,所以要进行重新运行。(多次运行后,每次都失败,可能是bug)

    ①安装:pip install pytest-rerunfailures

    ②运行:pytest --reruns N (N表示rerun的次数)

                  pytest --reruns N --reruns-delay M (M表示每运行一次,中间的间隔延迟时间)

    举个栗子:

    import pytest
    @pytest.fixture(scope="function")
    def function_fixt():
        #前置条件
        print("这是function级别前置条件")
        yield
        #后置清理
        print("这是function级别后置条件")
    
    @pytest.mark.demo
    class TestDemo():
        """测试用例类"""
    
        def test_demo1(self,function_fixt):
            assert 1==2  #断言不通过,进行rerun
    
        def test_demo2(self,function_fixt):
            pass
    

    运行pytest -m "demo" --reruns 3,结果如下:

     运行:pytest -m "demo" --reruns 3 --reruns-delay 5   的结果如下:

     rerun3次,加首次运行,一共会运行4次。

  • 相关阅读:
    状态同步和帧同步
    SVN和Git的使用
    客户端知识点
    客户端性能优化
    H5游戏开发面试经验
    2.0 pomelo-treasure官方demo的使用
    1.0 pomelo环境的搭建和部署
    python 网络编程
    冒泡排序
    面向对象-反射和元类
  • 原文地址:https://www.cnblogs.com/ananmy/p/13564025.html
Copyright © 2011-2022 走看看