2016-04-01 12 views
2

evalを使用することは悪い習慣であると一般に認められています。 this questionへの受け入れられた答えは、ほとんど常により良い選択肢があると述べています。しかし、標準ライブラリのtimeitモジュールがそれを使用しています。私はより良い選択肢が見つからない場合に遭遇しました。eval Unittestの主張のバージョン*良いアイデアですか?

unittest moduleは、それが失敗した場合は、必要に応じてmsgを印刷し、何かを主張することができ、フォーム

self.assert*(..., msg=None) 

のアサーション機能を持っています。これは

for i in range(1, 20): 
    self.assertEqual(foo(i), i, str(i) + ' failed') 

のようなコードが今fooは、例えば、一方では

def foo(i): 
    if i % 5 == 0: 
     raise ValueError() 
    return i 

その後、

  1. を例外を発生させることができた場合を考え、msgは印刷されません実行することができますassertEqualは技術的に反論の繰り返しと呼ばれることはありませんでした。
  2. 一方、基本的にfoo(i) == iは真実ではありませんでした(確かにfoo(i)は実行が完了していないため)msgを印刷すると便利です。

エラーの原因が例外であっても、msgを出力するバージョンを希望します。これにより、どの呼び出しが失敗したのかを正確に理解することができます。 evalを使用して、私はそのような(ちょうどポイントを説明するために、やや単純化したバージョンである)、以下のような文字列を取ったバージョンを書き込むことによってこれを行うことができます:

def assertEqual(lhs, rhs, msg=None): 
    try: 
     lhs_val = eval(lhs) 
     rhs_val = eval(rhs) 
     if lhs_val != rhs_val: 
      raise ValueError() 
    except: 
     if msg is not None: 
      print msg 
     raise 

、その後の

for i in range(1, 20): 
    self.assertEqual('foo(i)', 'i', str(i) + ' failed') 

を使用してもちろん技術的には、assert*への各コールをtry/except/finallyの中に置くことで完全に異なった方法で行うことも可能ですが、私は非常に冗長な選択肢しか考えられませんでした(msgを複製する必要もあります)。

ここではevalの正当な使用がありますか、それとも良い方法がありますか?

+1

これを行うことでどのような問題を解決しようとしていますか? – snakecharmerb

+0

@snakecharmerb質問に記載されているように、「This is running code like」の後にスニペットを実行し、失敗した反復で 'msg'を出力させます。 –

+0

確かに、それは既存の行動を得るよりもどうですか?私はあなたが無料で得るstacktraceと例外メッセージを読んでいる以上の利点を見ません。 – snakecharmerb

答えて

4

例外が発生した場合予期せずは、コードのバグを指摘します。あなたが単体テストで発見したいのとまったく同じです。単にではない、と同じではありません。修正する必要があることが判明したバグです。

あなたがいることを主張、例外が発生することを期待し場合:あなたが提起する例外で、使用期待していない場合は

with self.assertRaises(ValueError): 
    foo(i) 

:どちらかといえば

try: 
    foo(i) 
except ValueError: 
    self.fail("foo() raised ValueEror unexpectedly!") 

を、私は

self.assertEqualsAndCatch(foo, i, msg=...) 
のような独自のラッパーを書くことをお勧めします

私は:evalに文字列の代わりにコールバックとその引数を渡します。

+0

あなたの答えをありがとう。私はあなたがここで示唆しているのは、実行可能で冗長な代替案であると私の質問の最後から二番目の段落に書かれたものであると確信しています。私は 'try/except/finally'コンストラクトで囲まれたそれぞれの呼び出しを避けることを好むでしょう - それはコードのSNRを低下させます。 –

+0

私は冗長ではありません。与えられた、余分な行*がかかります。しかし、それは可読であり、失敗してはならないときに関連する情報を提供します。 decezeの答えで述べたように、あなたのunittestが実際に何かを発生させると、テストしたはずのバグを見つけたことになります。 – idjaw

+0

@Ami例外が発生すると期待しているときは何もアサーションしてはいけないと思います!それらの半分が*働かないことを知っているが、彼らが働くことを期待する* assertを書いていることを知っている20個の整数以上をループすることは変だ。肯定的な主張を取り除くと、重複するメッセージはなく、それほど冗長でもありません。 – deceze

関連する問題