8

私は現在、Python(3.1)を使用して小さなアプリケーションを作成しています。そして、いい子のように、ドキュメントテストを行っています。しかし、私はドクテストできないように見える方法に出くわしました。が含まれてinput()いるため、doctestの「期待する」部分に何を配置するかが完全にはわかりません。

私の問題を説明するためのサンプルコードは次のとおりです。

"""
>>> getFiveNums()
Howdy. Please enter five numbers, hit <enter> after each one
Please type in a number:
Please type in a number:
Please type in a number:
Please type in a number:
Please type in a number:
"""

import doctest

numbers = list()

# stores 5 user-entered numbers (strings, for now) in a list
def getFiveNums():
    print("Howdy. Please enter five numbers, hit <enter> after each one")
    for i in range(5):
        newNum = input("Please type in a number:")
        numbers.append(newNum)
    print("Here are your numbers: ", numbers)

if __name__ == "__main__":
    doctest.testmod(verbose=True)

doctestsを実行すると、「Expecting」セクションを出力した直後にプログラムの実行が停止し、5つの数字が次々に入力されるのを待って(プロンプトなしで)、続行します。以下に示すように:

doctestの結果

doctestのExpectingセクションに何を配置して、ユーザー入力を受け取って表示するメソッドをテストできるかはわかりません。だから私の質問は(最後に)、この関数は検証可能ですか?

4

5 に答える 5

7

これをテスト可能にする最も簡単な方法は、パラメーターの挿入です。

def getFiveNums(input_func=input):
    print("Howdy. Please enter five numbers, hit <enter> after each one")
    for i in range(5):
        newNum = input_func("Please type in a number:")
        numbers.append(newNum)
    print("Here are your numbers: ", numbers)

このように入力/出力の単体テストを実際に期待することはできません。呼び出しinputが何らかの理由で失敗する可能性があることを心配することはできません。最善のオプションは、何らかの性質のスタブメソッドを渡すことです。何かのようなもの

def fake_input(str):
    print(str)
    return 3

そのため、doctestでは、実際にテストしgetFiveNums(fake_input)ます。

input さらに、 nowへの直接の依存関係を解消することで、このコードを後でコマンドラインを使用しない別のコードに移植する場合は、新しいコードをドロップして入力を取得できます(ダイアログボックスであるかどうかは関係ありません)。 GUIアプリケーション、またはWebベースのアプリケーションのJavascriptポップアップなど)。

于 2010-05-01T01:53:34.720 に答える
6

doctestの回答を求めていることは承知していますが、このタイプの関数はdoctestの適切な候補ではない可能性があることをお勧めします。私はテストよりもドキュメンテーションにdoctestsを使用していますが、このためのdoctestは良いドキュメンテーションにはなりません。

団結したアプローチは次のようになります。

import unittest

# stores 5 user-entered numbers (strings, for now) in a list
def getFiveNums():
    numbers = []
    print "Howdy. Please enter five numbers, hit <enter> after each one"
    for i in range(5):
        newNum = input("Please type in a number:")
        numbers.append(newNum)
    return numbers

def mock_input(dummy_prompt):
    return 1

class TestGetFiveNums(unittest.TestCase):
    def setUp(self):
        self.saved_input = __builtins__.input
        __builtins__.input = mock_input

    def tearDown(self):
        __builtins__.input = self.saved_input

    def testGetFiveNums(self):
        printed_lines = getFiveNums()
        self.assertEquals(printed_lines, [1, 1, 1, 1, 1])

if __name__ == "__main__":
    unittest.main()

提案した機能を正確にテストしているわけではないかもしれませんが、アイデアは得られます。

于 2010-05-01T03:42:12.620 に答える
3

私は別の方法を見つけました。

"""
>>> get_five_nums(testing=True)
Howdy. Please enter five numbers, hit <enter> after each one.
Please type in a number: 1
Please type in a number: 1
Please type in a number: 1
Please type in a number: 1
Please type in a number: 1
Here is a list of the numbers you entered:  [1, 1, 1, 1, 1]
>>>
"""

import doctest

numbers = []

def get_five_nums(testing=False):
    """Stores 5 user-entered numbers (strings, for now) in a list."""

    print("Howdy. Please enter five numbers, hit <enter> after each one.")
    for i in range(5):
        new_num = int(input("Please type in a number: "))
        if testing:
            print(new_num)
        numbers.append(new_num)
    print("Here is a list of the numbers you entered: ", numbers)


if __name__ == "__main__":
    doctest.testmod(verbose=True)  

上記のコードをfoo.pyというファイルに保存します。次に、 input.txtというファイルを作成します。

必要なのはそれだけです。

1
1
1
1
1

5つ。各行に1つずつ。

プログラムをテストするには、ターミナルまたはコマンドプロンプトで次の手順を実行します(Macを使用しています)。

$ python foo.py <input.txt

これは、あらゆるプログラムのあらゆる種類のユーザー入力に対して簡単に変更できます。これにより、ターミナルセッションの出力をコピーして、doctestとして使用できるようになります。

注:ターミナルでの関数呼び出しはget_five_nums()になります。doctestでは、 get_five_nums(testing = True)である必要があります。

doctestはこのように使用することを意図していないように見えますが、それでも便利なハックです。

于 2010-10-27T03:54:13.133 に答える
2

私は不器用さに同意することができますが、それを少し少なくするために、あなたのために大部分の不器用さを保持する別の小さな関数を追加してみませんか(そしてあなたがそれにいる間にそれのテストを追加してください:)

doctestがこのタイプのテストに最適なソリューションではない可能性があることに同意しますが、ファイルを離れる必要がないという単純さ、またはテストを作成するときの関数さえも気に入っているTDDにdoctestを使用していることに気付きました。同様に、同じ方法でそのようなテストを実行したいと思うようになりました。とは言うものの、getFiveNums()の記述方法は、前述のパラメーターインジェクションなど、テストにより適したものに変更する必要があります。

def redirInput(*lines):
    """
    >>> import sys
    >>> redirInput('foo','bar')
    >>> sys.stdin.readline().strip()
    'foo'
    >>> sys.stdin.readline().strip()
    'bar'
    """
    import sys,io
    sys.stdin = io.StringIO(chr(10).join(lines))


def getFiveNums():
    """
    >>> redirInput('1','2','3','4','5')
    >>> getFineFums()
    ... rest as already written ...

于 2020-02-23T00:59:18.193 に答える
1

これが私が思いついた回避策です。少し厄介ですが、1行の入力のみが必要な場合に機能します。

def capitalize_name():
    """
    >>> import io, sys ; sys.stdin = io.StringIO("Bob")  # input
    >>> capitalize_name()
    What is your name?  Your name is BOB!
    """
    name = input('What is your name?  ')
    print('Your name is ' + name.upper() + '!')

残念ながら、入力に改行が含まれていると文句を言います(たとえば、「Bob \ nAlice」)。これはdoctestパーサーが圧倒されているためだと思います(しかし、はっきりとは言えません)。

chr(10)代わりに次のように使用することで、「\n」の問題を回避できます。

# stores 5 user-entered numbers (strings, for now) in a list
def getFiveNums():
    """
    >>> import io, sys ; sys.stdin = io.StringIO(chr(10).join(['1','2','3','4','5']))  # input
    >>> getFiveNums()
    Howdy. Please enter five numbers, hit <enter> after each one
    Please type in a number:Please type in a number:Please type in a number:Please type in a number:Please type in a number:Here are your numbers:  ['1', '2', '3', '4', '5']
    """
    print("Howdy. Please enter five numbers, hit <enter> after each one")
    numbers = []
    for _ in range(5):
        newNum = input("Please type in a number:")
        numbers.append(newNum)
    print("Here are your numbers: ", numbers)

これはさらに厄介ですが、機能します。(input()関数を介した)すべてのプロンプトテキストは、ユーザー入力を伴わずに出力として表示されることを覚えておく必要があります。(そのため、「数字を入力してください:」は、インスタンス間にスペースや改行を入れずに5回続けて表示されます。)

このソリューションは機能しますが、他のいくつかのソリューションよりも読みやすく、維持するのが難しいことに注意してください。これは、どのアプローチを使用するかを決定する際に考慮すべきことです。

于 2019-05-28T23:45:46.443 に答える