2012-07-08 7 views
5

私はユニットテストクラスをPythonでunittestを使用しています。私が理解しているように、unittestは、各テストの前にsetUp関数を呼び出すので、ユニットテストオブジェクトの状態は同じで、テストの実行順序は関係ありません。Pythonのユニットテストオブジェクト - オブジェクトがセットアップで上書きされていません

今私は、私は、このテストでは、test_translateのために失敗した... ...

#! usr/bin/python2 

from test import SpamTest 

import unittest 

class Spammer(unittest.TestCase): 

    def setUp(self): 
     self.st = SpamTest() 
     #self.st.numlist = [] <--TAKE NOTE OF ME! 
     self.st.add_num(1) 
     self.st.add_num(2) 
     self.st.add_num(3) 
     self.st.add_num(4) 

    def test_translate(self): 
     eggs = SpamTest([2, 3, 4, 5]) 
     self.st.incr(1) 
     self.assertTrue(self.st.__eq__(eggs)) 

    def test_set(self): 
     nl = [1, 4, 1, 5, 9] 
     self.st.numlist = nl 
     self.assertEqual(self.st.numlist, nl) 

if __name__ == "__main__": 
    tests = unittest.TestLoader().loadTestsFromTestCase(Spammer) 
    unittest.TextTestRunner(verbosity = 2).run(tests) 

を以下のユニットテストと

#! usr/bin/python2 

class SpamTest(object): 

    def __init__(self, numlist = []): 
     self.__numlist = numlist 

    @property 
    def numlist(self): 
     return self.__numlist 

    @numlist.setter 
    def numlist(self, numlist): 
     self.__numlist = numlist 

    def add_num(self, num): 
     self.__numlist.append(num) 

    def incr(self, delta): 
     self.numlist = map(lambda x: x + 1, self.numlist) 

    def __eq__(self, st2): 
     i = 0 
     limit = len(self.numlist) 

     if limit != len(st2.numlist): 
      return False 

     while i < limit: 
      if self.numlist[i] != st2.numlist[i]: 
       return False 

      i += 1 

     return True 

を、このクラスをテストしていています。

私はテストが成功させるために2つのことを行うことができます

(1)コメントを解除し、セットアップ機能の2行目。または

(2)translateが最初に発生するようにテストの名前を変更します。私はunittestがアルファベット順にテストを実行することに気づいた。 translateをたとえばatranslateに変更すると、最初にすべてのテストが成功するようになります。

(1)については、これが最初の行setUpの最初の行でself.stの新しいオブジェクトを作成するため、これがテストにどのように影響するかは想像もできません。 (2)については、setUpに私がself.stに新しいオブジェクトを割り当てているので、self.sttest_setというように私はtest_translateの結果に影響を与えてはならないので、私の苦情は似ています。

ここで私は何が不足していますか?

答えて

10

解決策の解説を勉強することなく、Fredrik LundhがDefault Parameter Values in Pythonを読んでください。

空のリストの問題をデフォルトの引数として説明している可能性があります。その理由は、明示的に後で明示的に空にしない限り、リストは初めてのみ空であるためです。初期の空のデフォルトリストは、明示的な引数が渡されないときに再利用されるリストタイプの単一インスタンスです。

上記の記事を読んで、デフォルトの引数について考えてみることをお勧めします。理由は論理的ですが、予期しないことがあります。

class SpamTest(object): 

    def __init__(self, numlist=None): 
     if numlist is None: 
      numlist = []   # this is the new instance -- the empty list 
     self.__numlist = numlist 
+0

だから私をもう少し教えてください。 'None None'と' == None'の違いは何ですか?私はいつも 'is None'を使いました。 '== None'は動作しませんでした。 – skytreader

+1

@skytreader:演算子 'is'はオブジェクトの同一性をテストします。 'None'値は、NoneTypeクラスの単一インスタンスによって表されます。値が「None」の場合は、同じオブジェクトへの参照を共有していることを意味します。 'numlist is None'は、同じオブジェクトが共有されているかどうかをテストしていることを意味します。 '=='演算子はもっと複雑です。 'numlist'が独自の' .__ eq__'メソッドを定義するクラスの(インスタンスへの)参照であった場合、 '=='は予期しないブール値を生成する可能性があります。しかし、単純なケースではほぼ同じです。 – pepr

4

これは、リストのようなMutableオブジェクトを使用するときにデフォルトパラメータがPythonで動作する方法によるものです。Default Parameter Values in Pythonです。ラインで

あなただけ SpamTestクラスのすべてのインスタンス間で共有されているリストの1つのインスタンスを持っているので、
def __init__(self, numlist = []): 

numlistのデフォルトのパラメータは一度だけ評価されます。

テストごとにテストsetUpが呼び出されても、新しい空のリストが作成されることはありません。また、そのリストインスタンスで動作するテストは、他のつま先で足踏みすることになります。

def __init__(self, numlist = None): 
    if numlist is None: 
     numlist = [] 
    self.__numlist = numlist 

プロパティを設定するとき、それは働く理由はあなたがブランドの新しい空のリストを提供することである:

修正がNoneのような非可変オブジェクトを使用して、代わりにこのような何かを持っていることですコンストラクタで作成されたリストを置き換えます。

+0

ああくそ:

一般的に推奨される修正は__init__のデフォルト値としてNoneを使用して、引数が、このように、渡されない場合は、本体内部の空のリストを設定することです。私の+1> :)あなたのソリューションをコピー/ペーストしたように見えます。しかし、それは本当に偶然のことです。たぶん 'numlist is None'だけが良いでしょう。 – pepr

+0

@pepr問題はありません。私はあなたからも良いフォームと正しい答えがあります。また、Fredrik Lundhの記事は本当に素晴らしいので、私たちは両方ともそれを言いました。 –

関連する問題