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

发布于 2024-08-28 09:54:59 字数 160 浏览 9 评论 0原文

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

  1. 测试用户输入。
  2. 测试从文件读取的输入。
  3. 测试从环境变量读取的输入。

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

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

浅暮の光 2024-09-04 09:54:59

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

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

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

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

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"))

会话如下:

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

Hello mark

那么您的测试将类似于:

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

请记住,您不必测试语言的 IO 设施(除非您是设计该语言的人,即完全不同的情况)。

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 really need to unit test Python's raw_input method? The open method? os.environ.get? No.

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_input or 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).

明月夜 2024-09-04 09:54:59

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

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

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

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'

这些断言将通过。请注意,side_effect 按顺序返回列表的元素。它还能做更多事情!我建议查看文档。

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:

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.

那支青花 2024-09-04 09:54:59

如果您可以在不使用外部流程的情况下摆脱困境,那就这样做。

然而,在某些情况下,这很复杂,并且您确实想要使用进程,例如,您想要测试 C 可执行文件的命令行界面。

用户输入

使用subprocess.Popen,如下所示:

process = subprocess.Popen(
    command,
    shell  = False,
    stdin  = subprocess.PIPE,
    stdout = subprocess.PIPE,
    stderr = subprocess.PIPE,
    universal_newlines = True
)
stdout, stderr = process.communicate("the user input\nline 2")
exit_status = process.wait()

从用户获取输入和从管道获取输入以使用raw_input<等方法完成输入没有区别/code> 或 sys.stdin.read()

文件

  • 创建一个临时目录并在测试 setUp 方法中创建您想要从中读取的文件:

    tdir = tempfile.mkdtemp(
        前缀 = '文件测试_', 
    )
    fpath = os.path.join(tdir,'文件名')
    fp = 打开(fpath,'w')
    fp.write("内容")
    fp.close()
    
  • 在测试中读取文件。

  • 之后删除临时目录。

    shutil.rmtree(tdir)
    
  • 从文件中读取数据是相当复杂的,大多数程序可以从文件中读取,也可以从 STDIN 中读取(例如使用 fileinput)。所以,如果你想测试的是输入特定内容时会发生什么,并且你的程序接受STDIN,只需使用Popen来测试程序。

环境变量

  • 使用 os.environ["THE_VAR"] = "the_val" 设置环境变量,
  • 使用 del os.environ["THE_VAR"] 取消设置code>
  • os.environ = {'a':'b'} 不起作用
  • 然后调用 subprocess.Popen。环境是从调用进程继承的。

模板代码

我在我的github上有一个模块< /a> 测试 STDOUTSTDERR 以及给定 STDIN 的退出状态、命令行参数和环境。另外,检查“tests”目录下该模块的测试。一定有更好的模块可以实现这一点,所以我的模块只是为了学习目的。

If you can get away without using an external process, do so.

However, there are situations where this is complicated, and you really want to use process, e.g., you want to test the command line interface of a C executable.

User input

Use subprocess.Popen as in:

process = subprocess.Popen(
    command,
    shell  = False,
    stdin  = subprocess.PIPE,
    stdout = subprocess.PIPE,
    stderr = subprocess.PIPE,
    universal_newlines = True
)
stdout, stderr = process.communicate("the user input\nline 2")
exit_status = process.wait()

There is no difference between taking input from a user and taking it from a pipe for input done with methods like raw_input or sys.stdin.read().

Files

  • Create a temporary directory and create the files you want to read from in there on your test setUp methods:

    tdir = tempfile.mkdtemp(
        prefix = 'filetest_', 
    )
    fpath = os.path.join(tdir,'filename')
    fp = open(fpath, 'w')
    fp.write("contents")
    fp.close()
    
  • Do the file reading in the tests.

  • Remove the temp dir afterwards.

    shutil.rmtree(tdir)
    
  • It is quite complicated reading from files, and most programs can read either from files or from STDIN (e.g. with fileinput). So, if what you want to test is what happens when a certain content is input, and your program accepts STDIN, just use Popen to test the program.

Environment variables

  • Set the environment variables with os.environ["THE_VAR"] = "the_val"
  • Unset them with del os.environ["THE_VAR"]
  • os.environ = {'a':'b'} does not work
  • Then call subprocess.Popen. The environment is inherited from the calling process.

Template Code

I have a module on my github that tests STDOUT, STDERR and the exit status given STDIN, command line arguments and enviroment. Also, check the tests for that module under the "tests" dir. There must be much better modules out there for this, so take mine just for learning purposes.

肥爪爪 2024-09-04 09:54:59

使用 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'

Using 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'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文