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

28 python unit-testing user-input

如何在出现以下条件的情况下编写测试:

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

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

Mar*_*off 32

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

真的需要单元测试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"))
Run Code Online (Sandbox Code Playgroud)

会话看起来像:

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")
Run Code Online (Sandbox Code Playgroud)

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

  • +1:这不仅使代码更易于测试,而且更加可重用,因为它不依赖于特定的输入源. (7认同)
  • 因此,这种方法如何与分配给多个变量的多个输入一起工作。您创建多个input_funcs还是有更优雅的方法? (2认同)

dbn*_*dbn 30

如果你使用raw_input(或任何其他特定的输入源),我是模拟库的主要支持者.鉴于Mark Rushakoff在他的例子中使用的代码:

def say_hello():
    name = raw_input("What is your name? ")
    return "Hello " + name
Run Code Online (Sandbox Code Playgroud)

您的测试代码可以使用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'
Run Code Online (Sandbox Code Playgroud)

这些断言会通过.请注意,side_effect按顺序返回列表的元素.它可以做得更多!我建议查看文档.

  • 对于Python 3,请注意`__builtin__`被重命名为`builtins`,`raw_input()`变为`input()`,因此:`mock.patch('builtins.input',return_value ='dew')` (16认同)
  • 我是dbw,不是露水:) (4认同)

Cir*_*四事件 5

如果您可以在不使用外部流程的情况下离开,请执行此操作.

但是,有些情况下这很复杂,并且您确实想要使用进程,例如,您想要测试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()
Run Code Online (Sandbox Code Playgroud)

从用户获取输入和从管道获取输入与使用raw_input或等方法完成输入之间没有区别sys.stdin.read().

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

    tdir = tempfile.mkdtemp(
        prefix = 'filetest_', 
    )
    fpath = os.path.join(tdir,'filename')
    fp = open(fpath, 'w')
    fp.write("contents")
    fp.close()
    
    Run Code Online (Sandbox Code Playgroud)
  • 在测试中读取文件.

  • 之后移除临时目录.

    shutil.rmtree(tdir)
    
    Run Code Online (Sandbox Code Playgroud)
  • 读取文件非常复杂,大多数程序可以从文件或STDIN中读取(例如fileinput).因此,如果要测试的是输入某个内容时发生的情况,并且您的程序接受STDIN,则只需使用Popen测试程序即可.

环境变量

  • 使用设置环境变量 os.environ["THE_VAR"] = "the_val"
  • 取消它们 del os.environ["THE_VAR"]
  • os.environ = {'a':'b'} 不起作用
  • 然后打电话subprocess.Popen.环境继承自调用进程.

模板代码

我在我的github上有一个模块,用于测试STDOUT,STDERR退出状态STDIN,命令行参数和环境.另外,在"tests"目录下检查该模块的测试.那里必须有更多更好的模块,所以我的目的只是为了学习目的.