2013-04-04 11 views
38

私はTDDを学び、ユニットテストを学び、モックをPythonで使ってみようとしました。ゆっくりとぶら下がっていますが、これを正しく実行しているかどうかは不明です。私はPython 2.4を使用しています。なぜなら、ベンダーAPIは事前にコンパイルされた2.4 pycファイルになっているからです。したがって、私は模擬0.8.0とunittest(unittest2ではなく)を使用しています。ユニットテストを使ってPythonでモックを正しく使う方法setUp

'mymodule.py '

私のテストケースファイル' test_myclass.py 'では、ldapオブジェクトをモックしたいと思います。 ldap.initializeはldap.ldapobject.SimpleLDAPObjectを返します。だから私はそれを模倣しなければならない方法だと思っていました。

  1. ん右に見える:
    import unittest 
    from ldap import INVALID_CREDENTIALS 
    from mock import patch, MagicMock 
    from mymodule import MyClass 
    
    class LDAPConnTests(unittest.TestCase): 
        @patch('ldap.initialize') 
        def setUp(self, mock_obj): 
         self.ldapserver = MyClass('myserver','myuser','mypass') 
         self.mocked_inst = mock_obj.return_value 
    
        def testRaisesMyCustomException(self): 
         self.mocked_inst.simple_bind_s = MagicMock() 
         # set our side effect to the ldap exception to raise 
         self.mocked_inst.simple_bind_s.side_effect = INVALID_CREDENTIALS 
         self.assertRaises(mymodule.MyCustomException, self.ldapserver.connect) 
    
        def testMyNextTestCase(self): 
         # blah blah 
    

    が質問のカップルに私をリード? :)
  2. これは、私がテストしているクラス内でインスタンス化されるオブジェクトをモックしようとする正しい方法ですか?
  3. setUpで@patchデコレータを呼び出すのは大丈夫ですか?これは奇妙な副作用を引き起こすでしょうか?
  4. テストケースファイルに例外をインポートせずにldap.INVALID_CREDENTIALS例外を発生させようとする模擬を取得する方法はありますか?
  5. 代わりにpatch.object()を使用する必要がありますか?

ありがとうございます。

+1

1-3)の代わりに '輸入ldap') 4 ...私には罰金だと' side_effect = ldap.INVALID_CREDENTIALS'を設定しますか? – Chris

+0

あなたはいつも同じテストを行うことができますが、より簡単なオブジェクトを自分で作ってみてください... – shackra

答えて

36

を参照してください:それはあなたがパッチ適用は、すべてのテストメソッドのために行われるようにしたい場合はパッチに設定にこのように設定する方が理にかなって26.5.3.4. Applying the same patch to every test method

+2

pre-Python3 Mock(http://www.voidspace.org.uk/python/mock/でホストされています)と同じことについては、 [すべてのテスト方法に同じパッチを適用する](http://www.voidspace.org.uk/python/mock/examples.html#applying-the-same-patch-to-every-test-method)。 – musiphil

+2

私は、TestCaseクラスでクラスレベルのモックを持っていて、 'setUp()'メソッドで呼び出しを行うときに既にそれがあると仮定していました。これはそうではありません;クラスレベルのモックは 'setUp()'で使用するために適時に適用されません。私は問題を、代わりに、すべてのテストで使用するヘルパーメソッドを作成することで解決しました。これが最良のアプローチであるかどうかは分かりませんが、それは機能します。 – berto

+0

@berto Answerであなたのコメントを拡大すると、私は役に立つと思う。それはここの他のものとは異なる、おそらくより簡単な解決策です。 – KobeJohn

4

あなたが適用するために、多くのパッチを持っている場合、あなたはこれを試してあまりにもセットアップ方法で初期化のものに適用するためにそれらをしたい:

def setUp(self): 
    self.patches = { 
     "sut.BaseTestRunner._acquire_slot": mock.Mock(), 
     "sut.GetResource": mock.Mock(spec=GetResource), 
     "sut.models": mock.Mock(spec=models), 
     "sut.DbApi": make_db_api_mock() 
    } 

    self.applied_patches = [mock.patch(patch, data) for patch, data in self.patches.items()] 
    [patch.apply for patch in self.applied_patches] 
    . 
    . rest of setup 
    . 


def tearDown(self): 
    patch.stop_all() 
+3

'tearDown()'に 'patch.stop_all()'を使うことを考えてください。 –

+0

私はこれを試しました - applied_pa​​tchesも開始する必要があるようです。次のような行を考えてみましょう。 'for patch in self.applied_pa​​tches:patch.start()' – F1Rumors

3

私はあなたの質問に答えることから始めましょう、そして私があげますpatch()setUp()がどのように対話するかの詳細な例です。

  1. 詳細については、answer#3を参照してください。
  2. はい、パッチを実際に呼び出すと、目的のオブジェクトをモックするように見えます。
  3. いいえ、@patch()デコレータをsetUp()に使用することはほとんどありません。オブジェクトはsetUp()で作成され、テストメソッド中に決して作成されないので、あなたは幸運にも得ました。
  4. モックオブジェクトをテストケースファイルにインポートせずに例外を発生させる方法はわかりません。
  5. ここにはpatch.object()の必要はありません。ターゲットを文字列として指定するのではなく、オブジェクトの属性にパッチを適用するだけです。

回答#3で展開するには、patch()デコレータは装飾機能の実行中のみ適用されるという問題があります。 setUp()が返ってくるとすぐに、パッチは削除されます。あなたのケースでは、それは動作しますが、私はこのテストを見て誰かを混乱させるでしょう。 setUp()の間に実際にパッチが発生するようにするには、withステートメントを使用して、パッチが削除されることを明らかにすることをお勧めします。

次の例には2つのテストケースがあります。 TestPatchAsDecoratorは、クラスをデコレートすると、テストメソッド中にパッチが適用されますが、setUp()ではパッチが適用されないことを示しています。 TestPatchInSetUpは、setUp()とテスト方法の両方でパッチが適用されるようにパッチを適用する方法を示しています。 self.addCleanUp()を呼び出すと、tearDown()の間にパッチが削除されることが確認されます。

import unittest 
from mock import patch 


@patch('__builtin__.sum', return_value=99) 
class TestPatchAsDecorator(unittest.TestCase): 
    def setUp(self): 
     s = sum([1, 2, 3]) 

     self.assertEqual(6, s) 

    def test_sum(self, mock_sum): 
     s1 = sum([1, 2, 3]) 
     mock_sum.return_value = 42 
     s2 = sum([1, 2, 3]) 

     self.assertEqual(99, s1) 
     self.assertEqual(42, s2) 


class TestPatchInSetUp(unittest.TestCase): 
    def setUp(self): 
     patcher = patch('__builtin__.sum', return_value=99) 
     self.mock_sum = patcher.start() 
     self.addCleanup(patcher.stop) 

     s = sum([1, 2, 3]) 

     self.assertEqual(99, s) 

    def test_sum(self): 
     s1 = sum([1, 2, 3]) 
     self.mock_sum.return_value = 42 
     s2 = sum([1, 2, 3]) 

     self.assertEqual(99, s1) 
     self.assertEqual(42, s2) 
関連する問題