2017-01-23 5 views
3

私は1つの入力(テキストファイル)を受け入れるPythonスクリプト./myprog.py file.txtを持っています。スクリプトは、指定された入力に基づいて文字列を出力します。スクリプトの出力をテストする

私は自分のプログラムをテストしたいテストファイルのセットを持っています。私は各ファイルの期待される出力を知っていて、スクリプトが各ファイルに対して正しい出力を生成していることを確認したい。

このタイプのテストを実行する一般的に受け入れられる方法は何ですか?

私はテストフレームワークとしてPythonのunittestモジュールを使用して考えて、その後、subprocess.check_output(stderr=subprocess.STDOUT)を通じて私のスクリプトを実行し、stdoutstderrを捕捉し、その後、実際と予想される文字列を比較するunittestassertEqualをしていました。私はいくつかのより良いソリューションが欠けていないことを確認したい。

+0

ユニットテストは通常​​、個々の関数やクラスで実行されます(通常、ファイルへの出力の書き込みやサブプロセスによる実行などは含まれません)。私は他の何かを推薦することはできませんが、チュートリアルをテストするPythonユニットを見て、それはおそらくこの質問をトピックから外すでしょう。 – byxor

+0

それはハードドライブ上のものを変更するので、それは技術的に単体テストではありませんが、とにかくそれを行うには大丈夫です。入力と出力のためにファイルのようなオブジェクトを取る関数の中で機能を移動することによって、コードをリファクタリングすることを検討してください。通常のコードはオープンファイルハンドルと 'sys.stdout'を渡します。しかし、あなたのユニットテストコードはStringIOバッファを使用します。ターミナルに接続されたパイプの代わりにstringioに書き込むときには違いがあります。だから、これは必ずしも最善の方法ではありません。あなたは決断する必要があります。 – tdelaney

+0

あなたはいつでも './myprog.py file.txt> result.txt'を実行し、後でツールを使ってファイルを比較することができます。 'diff result.txt expected.txt' - しかし、すべてをbash/batchスクリプトに入れても、テストとしてはあまり役に立ちません。 – furas

答えて

2

ここには2つの問題があります。関数のライブラリではなくプログラムをテストし、関数から返された値ではなく、出力するものをテストする。どちらもテストを難しくしているので、これらの問題を可能な限り解決することが最善です。

一般的な手法は、関数のライブラリを作成し、そのプログラムをその周りの薄いラッパーにすることです。これらの関数は結果を返し、プログラムだけが印刷を行います。つまり、ほとんどのコードに対して通常のユニットテスト手法を使用することができます。

ライブラリとプログラムの両方に1つのファイルを含めることができます。ここでは簡単な例をhello.pyとしています。最後のビットは、それがプログラムとして実行された場合は、それがライブラリとしてインポートされた場合は、ファイルを伝えることができる方法であることを

def hello(greeting, place): 
    return greeting + ", " + place + "!" 

def main(): 
    print(hello("Hello", "World")) 

if __name__ == '__main__': 
    main() 

。それはimport helloと個々の機能へのアクセスを可能にし、また、ファイルがプログラムとして実行することができます。 See this answer for more information

次に、ほぼ通常の単体テストを書くことができます。

ここ
import hello 
import unittest 
import sys 
from StringIO import StringIO 
import subprocess 

class TestHello(unittest.TestCase): 
    def test_hello(self): 
     self.assertEqual(
      hello.hello("foo", "bar"), 
      "foo, bar!" 
     ) 

    def test_main(self): 
     saved_stdout = sys.stdout 
     try: 
      out = StringIO() 
      sys.stdout = out 
      hello.main() 
      output = out.getvalue() 
      self.assertEqual(output, "Hello, World!\n") 
     finally: 
      sys.stdout = saved_stdout 

    def test_as_program(self): 
     self.assertEqual(
      subprocess.check_output(["python", "hello.py"]), 
      "Hello, World!\n" 
     ) 

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

test_hello直接関数として、ユニットテストhelloあります。より複雑なプログラムでは、テストする機能が増えます。また、ユニットテストmaintest_mainがあり、その出力をキャプチャするのにStringIOを使用しています。最後に、プログラムがtest_as_programのプログラムとして実行されるようにします。

重要なことは、データを返す関数として機能をテストし、印刷された文字列やフォーマットされた文字列としてできるだけテストしないことです。実際にプログラムをテストするまでには、mainが呼び出されていることを確認するだけです。

+0

大きな説明。モジュールは 'example.py'という名前で' import example'としてインポートするべきだと思います。 –

+0

@KshitijSaraogiちょうど見落としです。私がコードを作成していて、テストを再コピーしていない間に、その名前を 'example.py'から' hello.py'に変更しました。 – Schwern

+0

これは知っておきたいことです。私は、あなたの例を通して単体テストについていくつか新しいことを学びました。 –

関連する問題