【问题标题】:pytest new test for every iteration | for loop | parametrize fixturepytest 每次迭代的新测试 | for 循环 |参数化夹具
【发布时间】:2021-06-26 22:41:45
【问题描述】:

基本上,我正在尝试对路由列表的每次迭代进行测试,以检查与特定函数关联的网页是否返回有效状态代码。

我想要一些类似的东西:

import pytest
from flask import url_for
from myflaskapp import get_app

@pytest.yield_fixture
def app():
    # App context and specific overrides for test env
    yield get_app()

@pytest.yield_fixture
def client(app):
    yield app.test_client()

@pytest.yield_fixture
def routes(app):
    routes = [
    'foo',
    'bar',
    # There's quite a lot of function names here
    ]
    with app.app_context():
        for n, route in enumerate(routes):
            routes[n] = url_for(route)
            # yield url_for(route) #NOTE: This would be ideal, but not allowed.
            # convert the routes from func names to actual routes
    yield routes

@pytest.mark.parametrize('route', routes)
def test_page_load(client, route):
    assert client.get(route.endpoint).status_code == 200

我了解到,由于解释/加载/执行顺序的某些原因,您不能将参数化与固定装置混合作为参数,但是,如何根据“最佳实践”来解决?

我看到了一个解决方案,您可以直接从函数生成测试,这看起来非常灵活,可能符合我想要的Passing pytest fixture in parametrize(虽然我不能直接使用调用夹具装饰函数,所以可能不会)

虽然,我是 pytest 的新手,但我希望看到更多示例,说明如何在几乎没有限制的情况下生成测试或在迭代中执行多个测试,同时遵守正确的 pytest 样式和 DRY 原则。 (我知道conftest.py)

如果这很重要,我会优先考虑多功能性/实用性而不是适当的样式。 (在合理的范围内,可维护性也是一个高优先级)

我希望能够参考这个问题的解决方案,以帮助指导我在未来如何处理构建我的测试,但我似乎一直遇到障碍/限制或被 pytest 告知我不能做 X 解决方案我也期望/想要的方式。

相关帖子:

  1. 干:pytest: parameterize fixtures in a DRY way
  2. 从函数生成测试:Passing pytest fixture in parametrize
  3. 非常简单的解决方案(不适用于这种情况):Parametrize pytest fixture
  4. PyTest 中的 Flask 应用上下文:Testing code that requires a Flask app or request context
  5. 避免使用多个列表装置的边缘情况:Why does Pytest perform a nested loop over fixture parameters

【问题讨论】:

    标签: python flask pytest


    【解决方案1】:

    Pytest 固定装置本身可以参数化,但不能使用 pytest.mark.parametrize。 (看起来这种类型的问题也得到了回答here。)所以:

    import pytest
    from flask import url_for
    from myflaskapp import get_app
    
    @pytest.fixture
    def app():
        app = get_app()
        # app context stuff trimmed out here
        return app
    
    @pytest.fixture
    def client(app):
        client = app.test_client()
        return client
    
    @pytest.fixture(params=[
        'foo', 
        'bar'
    ])
    def route(request, app):
        '''GET method urls that we want to perform mass testing on'''
        with app.app_context():
            return url_for(request.param)
    
    
    def test_page_load(client, route):
        assert client.get(route.endpoint).status_code == 200
    

    The documentation 是这样解释的:

    可以对夹具函数进行参数化,在这种情况下它们将被多次调用,每次执行一组依赖测试,即。 e.依赖于此夹具的测试。测试函数通常不需要知道它们的重新运行。夹具参数化有助于为组件编写详尽的功能测试,这些组件本身可以通过多种方式进行配置。

    扩展前面的示例,我们可以标记该夹具以创建两个 smtp_connection 夹具实例,这将导致使用该夹具的所有测试运行两次。夹具函数通过特殊的请求对象访问每个参数:

    # content of conftest.py
    import pytest import smtplib
    
    
    @pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])
    def smtp_connection(request):
        smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
        yield smtp_connection
        print("finalizing {}".format(smtp_connection))
        smtp_connection.close()
    

    主要的变化是使用@pytest.fixture 声明参数,一个值的列表,每个fixture 函数将执行并且可以通过request.param 访问一个值。无需更改测试功能代码。

    【讨论】:

      【解决方案2】:

      我目前摸索的解决方案是这样的:

      import pytest
      from flask import url_for
      from myflaskapp import get_app
      
      @pytest.fixture
      def app():
          app = get_app()
          # app context stuff trimmed out here
          return app
      
      @pytest.fixture
      def client(app):
          client = app.test_client()
          return client
      
      def routes(app):
          '''GET method urls that we want to perform mass testing on'''
          routes = ['foo', 'bar']
          with app.app_context():
              for n, route in enumerate(routes):
                  routes[n] = url_for(route)
          return routes
      
      @pytest.mark.parametrize('route', routes(get_app()))
      #NOTE: It'd be really nice if I could use routes as a 
      # fixture and pytest would handle this for me. I feel like I'm
      # breaking the rules here doing it this way. (But I don't think I actually am)
      def test_page_load(client, route):
          assert client.get(route.endpoint).status_code == 200
      
      

      我对这个解决方案的最大问题是,我不能直接将夹具作为一个函数调用,而这个解决方案需要这样做,或者在夹具之外完成我的夹具所做的所有工作,这并不理想。我希望能够参考这个解决方案来解决我将来如何构建我的测试。

      对于任何希望专门复制我的烧瓶解决方案的人:

      我目前的解决方案对某些人来说可能比对我更糟糕,我为我的 get_app() 使用单例结构,所以如果在我的情况下多次调用 get_app() 应该没问题,因为它会调用 @ 987654324@ 并将应用程序本身存储为全局变量(如果尚未定义全局变量),基本上模拟仅调用一次 create_app() 的行为。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多