如何为 Python 单元测试提供标准输入、文件和环境变量输入?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2617057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-04 01:05:00  来源:igfitidea点击:

How to supply stdin, files and environment variable inputs to Python unit tests?

pythonunit-testinguser-input

提问by dbn

How to write tests where conditions like the following arise:

在出现以下情况时如何编写测试:

  1. Test user Input.
  2. Test input read from a file.
  3. Test input read from an environment variable.
  1. 测试用户输入。
  2. 测试从文件中读取的输入。
  3. 从环境变量读取的测试输入。

It'd be great if someone could show me how to approach the above mentioned scenarios; it'd still be awesome if you could point me to a few docs/articles/blog posts which I could read.

如果有人能告诉我如何处理上述场景,那就太好了;如果您能给我指出一些我可以阅读的文档/文章/博客文章,那仍然很棒。

回答by Mark Rushakoff

All three situations you've described are where you need to specifically go out of your way to ensure you are using loose coupling in your design.

您所描述的所有三种情况都是您需要特别注意的地方,以确保您在设计中使用松散耦合。

Do you reallyneed to unit test Python's raw_inputmethod? The openmethod? os.environ.get? No.

真的需要对 Python 的raw_input方法进行单元测试吗?该open方法? os.environ.get? 不。

You need to set up your design so that you can substitute other ways of retrieving that input. Then, during your unit tests, you'll throw in a stub of some sort that doesn't actually call raw_inputor open.

您需要设置您的设计,以便您可以用其他方式来检索该输入。然后,在您的单元测试期间,您将放入某种实际上并未调用raw_input或的存根open

For instance, your normal code might be something like:

例如,您的正常代码可能类似于:

import os
def say_hello(input_func):
    name = input_func()
    return "Hello " + name

def prompt_for_name():
    return raw_input("What is your name? ")

print say_hello(prompt_for_name)
# Normally would pass in methods, but lambdas can be used for brevity
print say_hello(lambda: open("a.txt").readline())
print say_hello(lambda: os.environ.get("USER"))

The session looks like:

会话看起来像:

What is your name? somebody
Hello somebody
Hello [some text]

Hello mark

Then your test will be like:

那么你的测试将是这样的:

def test_say_hello():
    output = say_hello(lambda: "test")
    assert(output == "Hello test")

Keep in mind that you should not have to test a language's IO facilities (unless you're the one designing the language, which is a different situation entirely).

请记住,您不应该测试语言的 IO 设施(除非您是设计语言的人,这完全是另一种情况)。

回答by dbn

If you are tied to using raw_input (or any other specific input source), I'm a big proponent of the mock library. Given the code that Mark Rushakoff used in his example:

如果您依赖于使用 raw_input(或任何其他特定输入源),那么我是模拟库的大力支持者。鉴于 Mark Rushakoff 在他的示例中使用的代码:

def say_hello():
    name = raw_input("What is your name? ")
    return "Hello " + name

Your test code could use mock:

您的测试代码可以使用模拟:

import mock

def test_say_hello():
     with mock.patch('__builtin__.raw_input', return_value='dbw'):
         assert say_hello() == 'Hello dbw'

     with mock.patch('__builtin__.raw_input', side_effect=['dbw', 'uki']):
         assert say_hello() == 'Hello dbw'
         assert say_hello() == 'Hello uki'

These assertions would pass. Note that side_effect returns the elements of the list in order. It can do so much more! I'd recommend checking out the documentation.

这些说法都会通过。注意 side_effect 按顺序返回列表的元素。它可以做得更多!我建议查看文档。

回答by famousgarkin

Using pytest:

使用pytest

import os


def test_user_input(monkeypatch):
    inputs = [10, 'y']
    input_generator = (i for i in inputs)
    monkeypatch.setattr('__builtin__.raw_input', lambda prompt: next(input_generator))
    assert raw_input('how many?') == 10
    assert raw_input('you sure?') == 'y'


def test_file_input(tmpdir):
    fixture = tmpdir.join('fixture.txt')
    fixture.write(os.linesep.join(['1', '2', '3']))
    fixture_path = str(fixture.realpath())
    with open(fixture_path) as f:
        assert f.readline() == '1' + os.linesep


def test_environment_input(monkeypatch):
    monkeypatch.setenv('STAGING', 1)
    assert os.environ['STAGING'] == '1'