如何在pytest中模拟字符串作为文件内容?

huangapple go评论53阅读模式
英文:

How to mock string as content of file for pytest?

问题

如何在pytest中将字符串传递给函数,以便受测试的函数将其视为文件内容?

我有一个解析文件的函数。文件是自定义文本格式。

我想要测试多个不同的输入文件。
最初的想法是这样使用pytest:

import pytest
import mymodule as t

# 这是一个有效的一行配置文件,从简单开始
@pytest.mark.parametrize("input_file", [
    "#",
    "##",
    "",
    "   ",
    "  ",
])

def test_is_input_file_valid(input_file):
    assert t.myclass(input_file)

我的问题是每行都需要是输入文件的内容,因为t.myclass(input_file)期望的是文件而不是字符串。所以,我需要以某种方式模拟它。

我假设pytest默认或通过插件具有此功能,但我无法找到它。

英文:

How can I provide string to function in pytest, so that function under test treat it as content of file ?

I have function that is parsing file. File is custom text format.

I want to test multiple different input files.
Initial idea was to use pytest is this way:

import pytest
import mymodule as t

# this is all valid one line config file, starting simple
@pytest.mark.parametrize("input_file", [
    "#",
    "##",
    "",
    "   ",
    "  ",
])

def test_is_input_file_valid(input_file):
    assert t.myclass(input_file)

Problem that I have is each line need to be content of input file, because t.myclass(input_file) is expecting file not string. So, I need somehow to mock it.

I am assuming pytest have this functionality by default or plugin, but was not able to find it.

答案1

得分: 1

我编写了一个名为 pytest_tmp_files 的 pytest 插件,用于解决这个确切的问题。以下是如何在你的示例中使用它的方式:

@pytest.mark.parametrize(
    'tmp_files', [
        {'f': '#'},
        {'f': '##'},
        {'f': ''},
        {'f': '   '},
        {'f': '  '},
    ],
    indirect=['tmp_files'],
)
def test_is_input_file_valid(tmp_files):
    assert t.myclass(tmp_files / 'f')

一些值得注意的事项:

  • 参数是字典,其中键是文件名,值是文件内容。这比仅指定文件内容略显冗长,但更加灵活。
  • 我假设你的解析器接受 pathlib.Path 对象,这就是 tmp_files / 'f' 所代表的。如果不是这样,你可能需要进行一些转换,但这应该很简单。
  • 指定的文件实际上是在一个真实的临时目录中创建的(每个测试用例都有一个唯一的临时目录),因此你可以像处理真实文件一样处理它们。
  • 我还编写了另一个 pytest 插件,叫做 parametrize_from_file,它允许你在结构化数据文件中(例如 YAML)中指定测试参数,与测试代码分开。我建议你也看看它。文件内容通常会变成长字符串,破坏参数列表的缩进,使整个测试脚本难以阅读。将这些参数移动到单独的文件中可以真正提高可读性。

另请参阅GitHub上的这个讨论,我尝试将此功能添加到 pytest 正式版本中。

英文:

I wrote a pytest plugin called pytest_tmp_files to solve this exact problem. Here's how it would look for your example:

@pytest.mark.parametrize(
    'tmp_files', [
        {'f': '#'},
        {'f': '##'},
        {'f': ''},
        {'f': '   '},
        {'f': '  '},
    ],
    indirect=['tmp_files'],
)
def test_is_input_file_valid(tmp_files):
    assert t.myclass(tmp_files / 'f')

Some things worth noting:

  • The parameters are dictionaries where the keys are file names and the values are files contents. This is a bit more verbose that just specifying the file contents, but much more flexible.
  • I'm assuming that your parser accepts pathlib.Path objects, which is what tmp_files / 'f' is. If not, you might have to do some sort of conversion here, but it should be straight forward.
  • The specified files are actually created in a real temporary directory (unique for each test case), so you can treat them exactly like real files.
  • I wrote another pytest plugin called parametrize_from_file that allows you to specify test parameters in structured data files (e.g. YAML), separate from the test code. I'd recommend checking it out as well. File contents often end up being long, multi-line strings that mess up the indentation of the parameter list and make the whole test script hard to read. Moving these parameters to a separate file can really help readability.

See also this discussion on GitHub, where I tried to get this feature added to pytest proper.

huangapple
  • 本文由 发表于 2023年6月2日 03:48:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76385243.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定