【问题标题】:Using fixtures at collect time in pytest在 pytest 中收集时使用固定装置
【发布时间】:2019-08-26 14:35:27
【问题描述】:

我将 testinfra 与 ansible 传输一起使用。它提供了host 夹具,它有ansible,所以我可以做host.ansible.get_variables()

现在我需要根据此库存中的值创建测试的参数化。

库存:

foo:
  hosts:
    foo1:
      somedata:
        - data1
        - data2

我想编写一个测试,测试清单中每个主机的 somedata 中的每个“数据”。 “每个主机”部分由 testnfra 处理,但我正在努力进行测试的参数化:

@pytest.fixture
def somedata(host):
    return host.ansible.get_variables()["somedata"]

@pytest.fixture(params=somedata):
def data(request):
    return request.param


def test_data(host, data):
   assert 'data' in data

两种方法我都试过了:

  • @pytest.fixture(params=somedata) -> TypeError: 'function' object is not iterable
  • @pytest.fixture(params=somedata()) -> Fixture "somedata" called directly. Fixtures are not meant to be called directly...

我该怎么做?我知道我无法在测试时更改测试数量,但我很确定我在收集时拥有相同的库存,所以理论上是可行的......

【问题讨论】:

    标签: pytest ansible-inventory testinfra


    【解决方案1】:

    在阅读了大量源代码后,我得出结论,在收集时调用夹具是不可能的。收集时没有固定装置,任何参数化都应该在调用任何测试之前发生。此外,在测试时无法更改测试数量(因此没有夹具可以更改)。

    回答我自己关于使用 Ansible 库存参数化测试函数的问题:这是可能的,但它需要手动读取库存、主机等。为此有一个特殊的钩子:pytest_generate_tests(它是一个函数,而不是一个夹具)。

    我当前获取host_interface 夹具参数化的任何测试的代码是:

    def cartesian(hosts, ar): 
        for host in hosts:
            for interface in ar.get_variables(host).get("interfaces",[]):
                yield (host, interface)
    
    def pytest_generate_tests(metafunc):
        if 'host_interface' in metafunc.fixturenames:
            inventory_file = metafunc.config.getoption('ansible_inventory')
            ansible_config = testinfra.utils.ansible_runner.get_ansible_config()
            inventory = testinfra.utils.ansible_runner.get_ansible_inventory(ansible_config, inventory_file)
            ar = testinfra.utils.ansible_runner.AnsibleRunner(inventory_file)
            hosts = ar.get_hosts(metafunc.config.option.hosts)
            metafunc.parametrize("host_interface", cartesian(hosts, ar))
    

    【讨论】:

      【解决方案2】:

      您应该使用辅助函数而不是夹具来参数化另一个夹具。 Fixtures 不能作为 pytest 中的装饰器参数。

      def somedata(host):
          return host.ansible.get_variables()["somedata"]
      
      @pytest.fixture(params=somedata()):
      def data(request):
          return request.param
      
      
      def test_data(host, data):
         assert 'data' in data
      

      这假设主机不是固定装置。

      如果主机是固定装置,则有解决问题的 hacky 方法。您应该将参数写入 tmp 文件或环境变量中,并使用辅助函数读取它。

      import os
      
      @pytest.fixture(autouse=True)
      def somedata(host):
          os.environ["host_param"] = host.ansible.get_variables()["somedata"]
      
      def get_params():
          return os.environ["host_param"] # do some clean up to return a list instead of a string    
      
      
      @pytest.fixture(params=get_params()):
      def data(request):
          return request.param
      
      
      def test_data(host, data):
         assert 'data' in data
      

      【讨论】:

      • 这行不通。 somedata 需要有人使用才能被调用至少一次。
      • 更新了夹具以添加自动使用。
      猜你喜欢
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 2021-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多