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

如何向Python单元测试提供stdin、文件和环境变量输入?,python,unit-testing,user-input,Python,Unit Testing,User Input,如何在出现以下情况时编写测试: 测试用户输入 从文件读取的测试输入 从环境变量读取的测试输入 如果有人能告诉我如何处理上述场景,那就太好了;如果你能给我指出一些我能看到的文档/文章/博客文章,那就太棒了 阅读。您描述的所有三种情况都需要特别注意,以确保在设计中使用松耦合 您真的需要单元测试Python的raw\u input方法吗?open方法操作系统环境获取?没有 您需要设置您的设计,以便可以替换检索该输入的其他方法。然后,在单元测试期间,您将插入某种存根,它实际上不会调用raw\u inpu

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

  • 测试用户输入
  • 从文件读取的测试输入
  • 从环境变量读取的测试输入
  • 如果有人能告诉我如何处理上述场景,那就太好了;如果你能给我指出一些我能看到的文档/文章/博客文章,那就太棒了
    阅读。

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

    您真的需要单元测试Python的
    raw\u input
    方法吗?
    open
    方法<代码>操作系统环境获取?没有

    您需要设置您的设计,以便可以替换检索该输入的其他方法。然后,在单元测试期间,您将插入某种存根,它实际上不会调用
    raw\u input
    open

    例如,您的普通代码可能类似于:

    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
    请记住,您不必测试一种语言的IO功能(除非您是设计该语言的人,这是一种完全不同的情况)。

    如果您不需要使用外部进程就可以脱身,请这样做

    然而,在有些情况下,这是复杂的,并且您确实希望使用进程,例如,您希望测试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\u 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()
      
    • 在测试中读取文件

    • 然后移除临时目录

      shutil.rmtree(tdir)
      
    • 从文件中读取是相当复杂的,大多数程序都可以从文件或STDIN中读取(例如使用
      fileinput
      )。因此,如果您想测试的是输入特定内容时发生的情况,并且您的程序接受STDIN,那么只需使用
      Popen
      测试程序

    环境变量

    • 使用
      os.environ[“the_VAR”]=“the_val”
    • 使用
      delos.environ[“THE_VAR”]
    • os.environ={'a':'b'}
      不工作
    • 然后调用
      subprocess.Popen
      。环境是从调用进程继承的
    模板代码


    我有一个模块测试STDOUT,
    STDERR
    ,以及给定的退出状态STDIN,命令行参数和环境。另外,在“tests”目录下检查该模块的测试。在这方面肯定有更好的模块,所以就拿我的模块来学习吧。

    如果你习惯于使用原始输入(或任何其他特定的输入源),我是这方面的大力支持者。鉴于Mark Rushakoff在其示例中使用的代码:

    def say_hello():
        name = raw_input("What is your name? ")
        return "Hello " + name
    
    您的测试代码可以使用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'
    
    这些断言将成为事实。注意,side_效应按顺序返回列表中的元素。它可以做更多!我建议您查看文档。

    使用:


    +1:这不仅使代码更易于测试,而且更易于重用,因为它不依赖于特定的输入源。除非您无法控制上述内容。好的路线通常是不可用的。那么这种方法如何处理分配给多个变量的多个输入呢。你是创建多输入函数还是有更优雅的方法?检查我是否正确理解你的问题:你想知道的是:如何测试一个可执行文件/脚本,给定一些输入,如stdin/环境变量,是否给出预期的输出,如stdout、stderr或exit status?@CiroSantilli新疆改造中心六四事件法轮功 我确实需要它,你知道怎么做吗?@johnballinarias请看我的答案:-)对于Python3,请注意,
    \uuuuu-builtin\uuuu
    被重命名为
    builtins
    ,而
    raw\u-input()
    变成了
    input()
    ,因此:
    mock.patch('builtins.input',return\u value='dew'))
    Mock是Python3中核心python的一部分,正如unittest import Mock中的
    一样。谢谢,我最喜欢这种方法(包括Python3变体)。另外,当使用Pytestas时,mock当然非常有用。据我所知,它只允许插入一次输入,如果有一个“while循环”从用户n次收集输入,该怎么办?@JohnBalvinArias除非涉及计时,否则无法区分管道和用户输入。如果有时间安排,请查看pexpect。
    
    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'
    
    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'