2016-08-09 7 views
4

私はスタンドアロンの入力(raw_input()とシンプルなプリントで読み込んでいます)で作業するPythonスクリプト(2.7)をテストしようとしていますが、これをどうやって見つけられないのでしょうか?問題は非常に簡単です。UnittestテストでPython Scriptで標準入力と標準出力をテストするにはどうしたらいいですか?

これが私のスクリプトの非常に非常に非常に履歴書コードです:

def example(): 
    number = raw_input() 
    print number 

if __name__ == '__main__': 
    example() 

私はこれをチェックするunittestのテストを書きたいが、私はどのように見つけることはありません。私はStringIOなどで試してみましたが、これを本当に簡単に行うための解決策は見つかりません。

誰かがアイデアを持っていますか?

PD:もちろん、実際のスクリプトでは、私はいくつかの行と他の種類のデータでデータブロックを使用します。

ありがとうございました。

EDIT:

は、私だけがインポートたStringIOをやっていたので、私は、StringIOのインポート少し問題があったと私は次のようにインポートするために必要な、それは完璧に動作しますが、最初は本当に特定の答えをどうもありがとうございますfrom StringIO import StringIO(私は本当になぜそれを理解していません)、それは可能性がありますように動作します。

しかし私はこの方法で別の問題を発見しました。私のプロジェクトでは、この方法でスクリプトをテストする必要があります(あなたのサポートのおかげで完璧に動作します)。 私は多くのファイルを持っていますテストを行って、ファイルを開き、結果ブロックで情報のブロックを読み込み、そのコードがブロックを処理して他のブロックと同じブロックを実行できるようにしたいと思います。このような..

何か:

class Test(unittest.TestCase): 
    ... 
    #open file and process saving data like datablocks and results 
    ... 
    allTest = True 
    for test in tests: 
     stub_stdin(self, test.dataBlock) 
     stub_stdouts(self) 
     runScrip() 
     if sys.stdout.getvalue() != test.expectResult: 
      allTest = False 

    self.assertEqual(allTest, True) 

私は多分unittestのが今の意味を持っていないことを知っている、しかし、あなたが行うことができます私が望むことについてのアイデア。だから、この方法は失敗し、私は理由を知らない。

答えて

4

標準的な技術は、標準sys.stdinsys.stdoutの希望の項目を嘲笑います。 Python 3との互換性を気にしない場合は、StringIOモジュールを使用することができます。しかし、先を考えてPython 2.7と3.3+に制限したい場合は、Python 2とPython 3の両方をこのようにサポートすることもできますioモジュールを介して多くの作業が行われます(ただし、少し変更する必要がありますが、この考えは今のところは間違いです)。

すでにunittest.TestCaseがあると仮定して、sys.stdin/sys.stdoutを置き換えたユーティリティ関数(または同じクラスのメソッド)を作成できます。まず輸入:

import sys 
import io 
import unittest 

が私の最近のプロジェクトの一つで、私はそれがユーザー(またはパイプを介して他のプログラム)はSTDINとしてあなたに入ります入力のstrを取るSTDIN、のためにこれをやりました:両方の場合において、それはテストケースインスタンスを受け入れ、そのaddCleanup方法THAを呼び出すこと

def stub_stdouts(testcase_inst): 
    stderr = sys.stderr 
    stdout = sys.stdout 

    def cleanup(): 
     sys.stderr = stderr 
     sys.stdout = stdout 

    testcase_inst.addCleanup(cleanup) 
    sys.stderr = StringIO() 
    sys.stdout = StringIO() 

注:stdoutとstderrとして

def stub_stdin(testcase_inst, inputs): 
    stdin = sys.stdin 

    def cleanup(): 
     sys.stdin = stdin 

    testcase_inst.addCleanup(cleanup) 
    sys.stdin = StringIO(inputs) 

tは、テストメソッドの期間が終了したときの位置にリセットする関数呼び出しを追加します。結果は、テストケースで最後に呼び出されてからsys.stdoutまでの間、友人はio.StringIOバージョンに置き換えられます。つまり、値を簡単に確認でき、混乱を避けることを心配する必要はありません後ろに。

例としてこれを示すことをお勧めします。 StringIOクラスがStringIOモジュールからのものである場合はPython 2には、このテストは唯一合格し、今

class ExampleTestCase(unittest.TestCase): 

    def test_example(self): 
     stub_stdin(self, '42') 
     stub_stdouts(self) 
     example() 
     self.assertEqual(sys.stdout.getvalue(), '42\n') 

を、とPython 3にはそのようなモジュールが存在しない:これを使用するには、単にそのようなテストケースを作成することができます。あなたができることは、ioモジュールのバージョンを使用して、入力を受け入れるという点で若干寛大になるように修正します。これにより、例外をトリガーするのではなく、ユニコードのエンコード/デコードが自動的に行われます(printステートメントPython 2でうまく動作しません。私は、通常のPython 2と3の間の相互互換性のため、この操作を行います。

class StringIO(io.StringIO): 
    """ 
    A "safely" wrapped version 
    """ 

    def __init__(self, value=''): 
     value = value.encode('utf8', 'backslashreplace').decode('utf8') 
     io.StringIO.__init__(self, value) 

    def write(self, msg): 
     io.StringIO.write(self, msg.encode(
      'utf8', 'backslashreplace').decode('utf8')) 

は今、あなたはあなたの自己は、ユニットテストに含まれます、一つのファイルにあなたの例の機能に加えて、この答えでは、すべてのコードはプラグのPython 2の両方で動作すると、 3(ただし、Python 3ではprintを関数として呼び出す必要があります)、stdioに対してテストを行います。

もう1つの注意:stub_ファンクションコールは、すべての単一テストメソッドで必要とされる場合はsetUpメソッドTestCaseに入れることができます。

もちろん、stdin/stdoutをスタブアウトするためにさまざまなモック関連ライブラリを使用したい場合は自由ですが、これはあなたの目標であれば外部依存関係に依存しません。あなたの第二の問題については


、テストケースは、彼らがメソッド内にカプセル化されなければならないとクラスレベルで、あなたの元の例は失敗しますない特定の方法、で記述される必要があります。しかし、あなたはこのような何かをしたいかもしれません:

class Test(unittest.TestCase): 

    def helper(self, data, answer, runner): 
     stub_stdin(self, data) 
     stub_stdouts(self) 
     runner() 
     self.assertEqual(sys.stdout.getvalue(), answer) 
     self.doCleanups() # optional, see comments below 

    def test_various_inputs(self): 
     data_and_answers = [ 
      ('hello', 'HELLOhello'), 
      ('goodbye', 'GOODBYEgoodbye'), 
     ] 

     runScript = upperlower # the function I want to test 

     for data, answer in data_and_answers: 
      self.helper(data, answer, runScript) 

あなたがdoCleanupsを呼び出したいかもしれない理由は、すべてのdata_and_answersペアがあるほど深くなってからクリーンアップスタックを防ぐためですが、それはオフにすべてをポップアップ表示されますクリーンアップのスタックので、最後にクリーンアップする必要がある他のものがあった場合、これは問題に終わる可能性があります - あなたはすべてのstdio関連オブジェクトが同じ順序で最後に復元されるので、 、本当のものはいつもそこにいます。今、私がテストしたかった機能:

def upperlower(): 
    raw = raw_input() 
    print (raw.upper() + raw), 

私は役立つかもしれなかった何のための説明のそうです、ビット:TestCaseクラス内で覚えては、フレームワークは、インスタンスのassertEqualや友人、それが機能するために厳密に依存しています。適切なレベルでのテストが確実に行われるようにするには、常にこれらのアサートを呼び出す必要があります。そのため、エラーが発生した瞬間に役立つエラーメッセージが表示されます。あなたがforループで何をしたかのような最後まで(何かが間違っていたことを教えてくれるが、何百ものうちどこが狂っているのか正確には分からない)。また、helperメソッド - testで始まらない限り、望むものを呼び出すことができます。なぜならフレームワークはそれを1つとして実行しようとし、ひどく失敗するからです。ですから、このコンベンションに従ってください。基本的に、テストケース内にテストを実行するためのテンプレートを用意することができます。私が行ったような入出力の束をループ内で使用することができます。

のみ(私は本当に理解していない私は、インポートたStringIOをやっていたので、私は、たStringIOのインポート少し問題を持っていたと私はStringIOをインポートたStringIOからのようにインポートするために必要な:あなたの他の質問については

なぜなら)、それがそうであるように、それは動作します。あなたは私の元のコードを見れば

まあ、私はimport ioを行なったし、その後、class StringIO(io.StringIO)を定義することでio.StringIOクラスをオーバーライドする方法をお見せしました。しかし、Python 2から厳密にこれをやっているので、これはあなたのために働きますが、私はPython 2が(おそらく間違いなく)5年以内にサポートされないことを考えれば、可能な限りPython 3への私の回答を対象にしようとします。あなたと同様の問題を抱えていたこの記事を読んでいる将来のユーザーを考えてみましょう。とにかく、元のfrom StringIO import StringIOは、StringIOモジュールのStringIOクラスのように機能します。 from cStringIO import StringIOは、StringIOモジュールのCバージョンをインポートするものとして機能するはずです。それらはすべて十分に近いインターフェイスを提供しているので、基本的には意図どおりに動作します(もちろん、これをPython 3で実行しようとするまで)。

もう一度このコードを私のコードとともに使用すると、self-contained working test scriptになります。ドキュメンテーションを見て、コードの形に従ってください。また、独自の構文を作り出すのではなく、動作することを期待してください(そして、コードが正しく機能しなかった理由は、テストコードはクラスが構築されているので、Pythonがあなたのモジュールをインポートしている間にそのすべてが実行されました。テストを実行するために必要なものもありません(つまり、クラス自体はまだ存在しません)。ただの苦しみに合わせて死ぬ)。あなたが直面している問題は本当に一般的なものですが、あなたの正確な問題を検索するための素早く簡単な名前を持たないのであれば、どこに間違っていたのか把握するのが難しいでしょうか? :)とにかく幸運と、あなたのコードをテストする努力を取って良いです。


他の方法もありますが、ここで私が見た他の質問/回答は役に立たないようですが、私はこれを願っています。参照のために他のもの:

、それはこのの全てがパイソン3.3+またはoriginal/rolling backport version on pypiで利用可能unittest.mockを用いて行われ、それを与えることができることを繰り返すむきますこれらのライブラリは、実際に何が起こったかについて複雑な部分を隠してしまい、実際に何が起きるか(または起こる必要があるか)、またはリダイレクトが実際にどのように起こるかについての詳細を隠すことになります。必要ならば、unittest.mock.patchを読んで、StringIOパッチsys.stdoutセクションまで少し下に行くことができます。

+0

本当に良い答えをいただきありがとうございます。@metatoaster私は、あなたがforループで読むためにあなたの方法を使って見つけた問題について、より多くの情報を追加しました。ありがとうございました。 –

関連する問題