如何测试一个不包含函数但有多个输入调用的Python文件?

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

How to test a python file that contains no functions and has multiple input calls?

问题

以下是您要翻译的内容:

I have the following python file ```two_strings.py```:

str1 = input()
str2 = input()
combined = str1 + "," + str2
print(combined)


I then have a jupyter notebook file called ```test_two_strings.ipynb```:

import sys
import unittest
from unittest.mock import patch
import two_strings
from io import StringIO

sys.path.append('Challenge10/')

class SimpleTestCase(unittest.TestCase):
def test_defs(self):
defs = [v for v in dir(two_strings) if v[0] != '_']
assert 'str1' in defs, f'Definition for str1 not found'
assert 'str2' in defs, f'Definition for str2 not found'
assert 'combined' in defs, f'Definition for combined not found'

def test_inputs_outputs(self):
    user_input = [
        ['a','b'],
        ['10','sixty six'],
        ['hello','there']
        ['once upon a time','in fairyland']
    ]
    expected_output = [
        'a,b',
        '10,sixty six',
        'hello,there',
        'once upon a time,in fairyland'
    ]
    for i in range(len(user_input)):
        with patch('builtins.input', side_effect=user_input[i]):
            output = StringIO()
            with patch('sys.stdout', new=output):
                two_strings.main()
                self.assertEqual(output.getvalue().strip(), expected_output[i])

if name == 'main':
unittest.main(argv=['-v'],verbosity=2, exit=False)


这是您要求的翻译结果。如果您需要任何其他帮助,请随时提问。

<details>
<summary>英文:</summary>

I have the following python file ```two_strings.py```:

str1 = input()
str2 = input()
combined = str1 + "," + str2
print(combined)

I then have a jupyter notebook file called ```test_two_strings.ipynb```:

import sys
import unittest
from unittest.mock import patch
import two_strings
from io import StringIO

sys.path.append('Challenge10/')

class SimpleTestCase(unittest.TestCase):
def test_defs(self):
defs = [v for v in dir(two_strings) if v[0] != '_']
assert 'str1' in defs, f'Definition for str1 not found'
assert 'str2' in defs, f'Definition for str2 not found'
assert 'combined' in defs, f'Definition for combined not found'

def test_inputs_outputs(self):
    user_input = [
        [&#39;a&#39;,&#39;b&#39;],
        [&#39;10&#39;,&#39;sixty six&#39;],
        [&#39;hello&#39;,&#39;there&#39;]
        [&#39;once upon a time&#39;,&#39;in fairyland&#39;]
    ]
    expected_output = [
        &#39;a,b&#39;,
        &#39;10,sixty six&#39;,
        &#39;hello,there&#39;,
        &#39;once upon a time,in fairyland&#39;
    ]
    for i in range(len(user_input)):
        with patch(&#39;builtins.input&#39;, side_effect=user_input[i]):
            output = StringIO()
            with patch(&#39;sys.stdout&#39;, new=output):
                two_strings.main()
                self.assertEqual(output.getvalue().strip(), expected_output[i])

if name == 'main':
unittest.main(argv=['-v'],verbosity=2, exit=False)

That will test the python file within the notebook file. I am using jupyterlite. However I am unable to figure out exactly how to setup unit tests for testing multiple lines of input() calls and making sure the output is correct. I know the expected output is correct but knowing how janky using input in jupyter notebooks are in jupyterlite, I want to know if this is possible? If so, how?

</details>


# 答案1
**得分**: 0

以下是翻译好的部分:

适当处理`input()`的方法是将其抽象化。将`input()`移到一个函数中,并使其返回一个字符串元组,类似于:

```python
def get_inputs():
  return input(), input()

然后,您可以模拟该函数并使其返回硬编码(或测试生成的)字符串,然后您的测试可以用于验证。

一般来说,单元测试不应以任何方式依赖input()。您应该摆脱这种依赖。

但是,如果您确实必须按原样测试脚本,即使包括input(),那么您必须自动传递输入给脚本。这意味着您不能import脚本,而您的替代方案都不是很好。总的来说,我要说的通常会让我拉开一个开发者,教他们如何正确编写代码。这不是一个好主意。尽量不要做我即将写下的事情。

在这里,您有两种选择:execsubprocess

对于exec,您基本上是在当前作用域中以字符串形式运行Python代码。因此,您可以自己模拟input,直接将本地字典修改为您可以监视的东西,并以此方式使事情正常工作。

def test_something(self):
  # 模拟 `input()`
  new_input = lambda: 'some string'

  # 创建一个可以监视的本地字典
  new_locals = {'input': new_input}

  # 执行要测试的代码
  exec(open('two_strings.py').read(), globals(), new_locals)

  # 检查创建的本地变量是否包含您想要的值
  self.assertEqual(new_locals['combined'], 'some string,some string')

对于subprocess,代码有点复杂,所以我将其省略,并留给您填写缺失的部分。但是,一般的思路是在自己的子进程中运行脚本,提供输入,然后监视输出。

如果您实际上没有将要测试的结果存储在可以从测试中访问的变量中,那么您应该更喜欢使用subprocess。例如,如果您的打印不是print(combined)而是print(str1 + "," + str2)

尽管如此,运行这样的测试有点疯狂。

英文:

The proper way to work around usage of input() is to abstract it away. Move the input() into a function and have it return a tuple of strings. Something like:

def get_inputs():
  return input(), input()

You can then mock out the function and have it return hard-coded (or test-generated) strings that your test then uses for validation.

In general, you should not have a unit test rely on input() in any way. You want to get rid of that stuff.

If, however, you absolutely must test the script as-is even with input(), then you'll have to automate passing in input to the script. Which means you can't import the script and your alternatives are all bad. In general, what I'm saying can be done here would typically cause me to pull a developer aside and teach them how to properly code. This is not a good idea. Do your best to not do what I'm about to write down.

You have kinda-sorta two choices here: exec and subprocess.

For exec, you are basically running python code as a string in the current scope. So, you can do your own poor-man's mock of input, direct local dictionary modifications to something you can monitor, and get things working like that.

def test_something(self):
  # Mock out `input()`
  new_input = lambda: &#39;some string&#39;

  # Create a locals dictionary you can monitor
  new_locals = {&#39;input&#39;: new_input}

  # Execute the code you want to test
  exec(open(&#39;two_strings.py&#39;).read(), globals(), new_locals)

  # Check the created local variable for the value you want
  self.assertEqual(new_locals[&#39;combined&#39;], &#39;some string,some string&#39;)

For subprocess the code is a bit complicated so I'll elide it and leave it as an exercise for you to fill in the blanks. But, the general idea would be to run the script in its own subprocess, feed it input, and then monitor the output.

You should prefer to use subprocess if you don't actually store the result you want to test in a variable you can access from the test. For example, if your print wasn't print(combined) but was instead print(str1 + &quot;,&quot; + str2).

That being said, running a test like this is a bit crazy.

答案2

得分: 0

以下是翻译好的代码部分:

import sys
from io import StringIO
import unittest

challengeFile = 'challenge.py'
fileRunner = open(challengeFile)
code = fileRunner.read()

class TestName(unittest.TestCase):
    def get_stdout(self, inputs):
        original_stdin = sys.stdin
        original_stdout = sys.stdout
        test_inputs = inputs
        sys.stdin = StringIO('\n'.join(test_inputs))
        sys.stdout = StringIO()
        exec(code)
        script_output = sys.stdout.getvalue()
        sys.stdin = original_stdin
        sys.stdout = original_stdout
        return script_output
    
    def correct(self, test_output, expected_output):
        self.assertEqual(str(test_output).strip(), str(expected_output).strip())
    
    def test_challenge_1(self):
        self.correct(self.get_stdout(["1 1 1 1 1 1"]), "6")

    def test_challenge_2(self):
        self.correct(self.get_stdout(["1 5"]), "6")

    def test_challenge_3(self):
        self.correct(self.get_stdout(["8 11"]), "19")
        
    fileRunner.close()
    
unittest.main(exit=False)

这部分代码主要是一个Python测试脚本,用于测试以下的代码(注意这里没有函数):

s = input().split()
sList = list(map(lambda x: int(x), s))
print(sum(sList))
英文:

After many days of testing and looking through documentation it is actually quite simple, as pythons sys module allows individuals to directly interact with stdout and stdin:

import sys
from io import StringIO
import unittest

challengeFile = &#39;challenge.py&#39;
fileRunner = open(challengeFile)
code = fileRunner.read()

class TestName(unittest.TestCase):
    def get_stdout(self, inputs):
        original_stdin = sys.stdin
        original_stdout = sys.stdout
        test_inputs = inputs
        sys.stdin = StringIO(&#39;\n&#39;.join(test_inputs))
        sys.stdout = StringIO()
        exec(code)
        script_output = sys.stdout.getvalue()
        sys.stdin = original_stdin
        sys.stdout = original_stdout
        return script_output
    
    def correct(self, test_output, expected_output):
        self.assertEqual(str(test_output).strip(), str(expected_output).strip())
    
    def test_challenge_1(self):
        self.correct(self.get_stdout([&quot;1 1 1 1 1 1&quot;]), &quot;6&quot;)

    def test_challenge_2(self):
        self.correct(self.get_stdout([&quot;1 5&quot;]), &quot;6&quot;)

    def test_challenge_3(self):
        self.correct(self.get_stdout([&quot;8 11&quot;]), &quot;19&quot;)
        
    fileRunner.close()
    
unittest.main(exit=False)

This will test the following code (notice how there are no functions!):

s = input().split()
sList = list(map(lambda x: int(x), s))
print(sum(sList))

huangapple
  • 本文由 发表于 2023年5月21日 08:35:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76297850.html
匿名

发表评论

匿名网友

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

确定