【问题标题】:How can I prevent the value of a fixture to be printed?如何防止打印夹具的值?
【发布时间】:2020-10-27 08:02:34
【问题描述】:

我创建了一个获取令牌的 pytest 固定装置。当使用此夹具的测试失败时,令牌将打印在日志中。一方面没有帮助,另一方面是安全问题。

如何防止打印灯具内容?

MVCE

import pytest

@pytest.fixture
def token():
    yield "secret"

def test_foo(token):
    assert False

显示"secret"

【问题讨论】:

    标签: python security pytest fixtures


    【解决方案1】:

    最简单的解决方案是更改回溯格式,例如pytest --tb=short 将省略打印测试函数 args。这也可以持久化在pytest.ini中,有效地修改了默认的pytest调用:

    [pytest]
    addopts = --tb=short
    

    不过,您也可以通过扩展 pytest 来自定义输出。

    从技术上讲,pytest 打印到终端的所有内容都包含在TestReport 中,因此您可以在测试完成后,但在打印失败摘要之前修改报告对象。示例代码,放在项目中的conftest.py 或测试根目录:

    def pytest_runtest_logreport(report):
        if report.longrepr is None:
            return
        for tb_repr, *_ in report.longrepr.chain:
            for entry in tb_repr.reprentries:
                if entry.reprfuncargs is not None:
                    args = entry.reprfuncargs.args
                    for idx, (name, value) in enumerate(args):
                        if name == "token":
                            args[idx] = (name, "********")
                if entry.reprlocals is not None:
                    lines = entry.reprlocals.lines
                    for idx, line in enumerate(lines):
                        if line.startswith("token"):
                            lines[idx] = "token          = '*********'"
    

    虽然笨拙且未经测试,但这演示了该方法:获取存储在报告中的回溯信息,如果任何条目具有可用的reprfuncargs(这包含所有测试函数参数的值,包括固定装置),修改token存在时的价值。对reprlocals 执行相同操作(那些是记录帧的f_locals,并在您调用例如pytest --showlocals 时打印出来)。

    现在运行测试时,您应该得到修改后的错误输出,如

    ===== FAILURES =====
    _____ test_foo _____
    
    token = ********
    
        def test_foo(token):
    >       assert False
    E       assert False
    

    pytest_runtest_logreport 挂钩用于在实际报告开始之前对在pytest_runtest_makereport 中创建的报告对象进行后处理。

    【讨论】:

      【解决方案2】:

      一种效果很好的方法,改编自here

      import pytest
      
      class Secret:
          def __init__(self, value):
              self.value = value
      
          def __repr__(self):
              return "Secret(********)"
      
          def __str___(self):
              return "*******"
      
      def get_from_vault(key):
          return "something looked up in vault"
      
      
      @pytest.fixture(scope='session')
      def password():
          return Secret(get_from_vault("key_in_value"))
      
      def login(username, password):
          pass
      
      def test_using_password(password):
          # reference the value directly in a function
          login("username", password.value)
          # If you use the value directly in an assert, it'll still log if it fails
          assert "something looked up in vault" == password.value
      
          # but won't be printed here
          assert False
      
      

      这并不完美,但会更简单。这是输出:

      ==================================================================================== FAILURES ====================================================================================
      ______________________________________________________________________________ test_using_password _______________________________________________________________________________
      
      password = Secret(********)
      
          def test_using_password(password):
              # reference the value directly in a function
              login("username", password.value)
              # If you use the value directly in an assert, it'll still log if it fails
              assert "something looked up in vault" == password.value
          
              # but won't be printed here
      >       assert False
      E       assert False
      
      test_stuff.py:31: AssertionError
      ============================================================================ short test summary info =============================================================================
      FAILED test_stuff.py::test_using_password - assert False
      

      【讨论】:

        【解决方案3】:

        您也可以按照此处建议的方法进行操作:https://github.com/pytest-dev/pytest/issues/8613#issuecomment-830011874

        1. 将值包装在一个对象中,以防止其意外转义(__str____repr__ 返回混淆值)
        2. 使用该包装器代替原始字符串,仅在需要时解包。

        【讨论】:

          猜你喜欢
          • 2014-12-09
          • 1970-01-01
          • 2010-10-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多