2013-04-11 438 views
15

私はpythonerです。最近、私は自分のプロジェクトの中でいくつかのコアモジュールについてより完全な単体テストを行うようになっています。 私たちは常に 'assertEqual'、 'assertTrue'などのメソッドを使って単体テストを行うので、これらのメソッドはすべてテスト対象の関数からの戻り値を必要とします。返り値なしでいくつかの関数でプレーンな単体テストを行う方法は不思議です。Pythonでは、戻り値のない関数に対して単体テストを行う方法は?

HelloTestで関数def foo(self、msg)をどのようにテストするか、ここに小さな例を示したいと思いますか?

class HelloTest(object): 
    def foo(self, msg): 
     MSG = msg.upper() 
     self.bar(MSG) 

    def bar(self, MSG): 
     print MSG 

答えて

5

この特定のケースでは、私はプリントを模倣してから、私のアサーションでモックを使用します。

Pythonでは、Mock packageを使用して模擬します。 Pythonの3では

+0

+1: '模擬(mock) 'へのポインタです。 –

+2

もし、彼がPython 3の下で何かを使っているのであれば、印刷を模倣するのは簡単ではありません。彼はsys.stdoutをモックすることができましたが、バーを変更する必要があります – aychedee

2

、あなたはtell print where to print toできる:

プリント(*オブジェクト、9月=」」、エンド= '\ n' は、ファイル= sys.stdoutの、偽=フラッシュ)

だからオプションの引数を追加:通常の使用では

def bar(self, MSG, file=sys.stdout): 
    print(MSG, file=file) 

を、それが標準出力に出力しますが、ユニットテストのためにあなた自身のファイルを渡すことができます。

のPython 2では、それはビットメシエですが、あなたのことができredirect stdout to a StringIO buffer

import StringIO 
import sys 

out = StringIO.StringIO() 
sys.stdout = out 

# run unit tests 

sys.stdout = sys.__stdout__ 

# check the contents of `out` 
+1

出力付き関数の単体テストを行うための良いソリューションです。しかし、私が最も気にしているのは 'def foo(self、msg)'をテストする方法です。なぜならすべての関数がstdoutで何かをするわけではないからです – Yarkee

2

コードは上記と同じタスクを実行している下記のようになります

class HelloTest(object): 

    def foo(self, msg): 
     self.msg = msg.upper() 
     self.bar() 

    def bar(self): 
     print self.msg 

ユニットテストは次のとおりです。

from hello import HelloTest 
import unittest 

class TestFoo(unittest.TestCase): 
    def test_foo_case(self): 
     msg = "test" 
     ob = HelloTest() 
     ob.foo(msg) 
     expected = "TEST" 
     self.assertEqual(ob.msg, expected) 

if __name__ == '__main__': 
    unittest.main(exit=False) 
9

もう1つの答えとして、Pythonモックライブラリを使用して、関数/メソッド

from mock import patch 
from my_module import HelloTest 
import unittest 

class TestFoo(unittest.TestCase): 

    @patch('hello.HelloTest.bar') 
    def test_foo_case(self, mock_bar): 

     ht = HelloTest() 

     ht.foo("some string") 
     self.assertEqual(ob.msg, "SOME STRING") 
     self.assertTrue(mock_bar.called) 

への呼び出しは、これがHelloTestにbar方法をパッチし、それに対する呼び出しを記録モックオブジェクトに置き換えます。

モッキングは少しウサギの穴です。あなたのテストが脆弱になるので、絶対にしなければならないときにだけ行います。たとえば、モックされたオブジェクトのAPIの変更には気付かないでしょう。 @Jordanの導入に

2

おかげで、私はこれをコーディングし、それがHelloTest.foo

from mock import Mock 
import unittest 


class HelloTestTestCase(unittest.TestCase): 
    def setUp(self): 
     self.hello_test = HelloTest() 

    def tearDown(self): 
     pass 

    def test_foo(self): 
     msg = 'hello' 
     expected_bar_arg = 'HELLO' 
     self.hello_test.bar = Mock() 

     self.hello_test.foo(msg) 
     self.hello_test.bar.assert_called_once_with(expected_bar_arg) 


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

のための実行可能なユニットテストだと思う誰もがそのfooのコールバーをチェックしたい、なぜ私はかなり理解していません。

Fooにはいくつかの機能があり、この機能をテストする必要があります。 fooがこれを行うためにbarを使用しているなら、私の心配ではないはずです。

foo(msg)が呼び出された後に、msg.upper()がstdoutに送信されるという結果が得られます。

redirect stdout to a string bufferこの文字列バッファの内容が期待どおりであるかどうか確認してください。

例:

import sys 
import unittest 
from io import TextIOWrapper, BytesIO 

class TestScript(unittest.TestCase): 
    def setUp(self): 
     self._old_stdout = sys.stdout 
     sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding) 

    def _output(self): 
     self._stdout.seek(0) 
     return self._stdout.read() 

    def test_foo(self): 
     hello_test = HelloTest() 
     hello_test.foo("blub") 
     self.assertEqual(self._output(), "BLUB") 

    def tearDown(self): 
     sys.stdout = self._old_stdout 
     self._stdout.close() 

ます。また、標準入力のために(といくつかの入力を模擬するために標準入力に書き込む)ことを行うことができます、あなたが行われるために特別な何かを必要とする場合は、非Unicodeテキストを許可するように、TestIOWrapperをサブクラス化することができますsys.stdout.buffer(Python 2 vs. Python 3)を使用せずにsys.stdoutに送信されます。 その例はthis SO answerです。 (まだ)Python 2のみを使用している場合は、StringIOを使用する方がioモジュールを使用するよりも良いかもしれません。

関連する問題