2011-12-29 12 views
46

私のモジュールからのエラーメッセージが有益であることを確認するために、私はassertRaises()によってキャッチされたすべてのエラーメッセージを見たいと思います。今日私は各assertRaises()のためにそれを行いますが、テストコードにはたくさんあるので非常に面倒です。Python2.7のunittestでassertRaises()がキャッチしたエラーメッセージを表示するには?

すべてのassertRaises()に対してエラーメッセージを出力するにはどうすればよいですか?私はhttp://docs.python.org/library/unittest.htmlのドキュメントをどのように解決するかを考えずに研究しました。どういうわけかassertRaises()メソッドをmonkeypatchできますか?私は、テストコードを標準的な方法で使用することが最も多いので、テストコード内のすべてのassertRaises()行を変更しないことをお勧めします。

私はこの質問はPython unittest: how do I test the argument in an Exceptions?

に関連している。これは私が今日それを行う方法であると思います。例えば:

#!/usr/bin/env python 

def fail(): 
    raise ValueError('Misspellled errrorr messageee') 

とテストコード:

#!/usr/bin/env python 
import unittest 
import failure 

class TestFailureModule(unittest.TestCase): 

    def testFail(self): 
     self.assertRaises(ValueError, failure.fail) 

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

エラーメッセージをチェックするために、私は単に例えば例外IOErrorに)(assertRaisesにエラーの種類を変更します。次に、エラーメッセージが表示されます。

E 
====================================================================== 
ERROR: testFail (__main__.TestFailureModule) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
File "test_failure.py", line 8, in testFail 
    self.assertRaises(IOError, failure.fail) 
    File "/usr/lib/python2.7/unittest/case.py", line 471, in assertRaises 
    callableObj(*args, **kwargs) 
File "/home/jonas/Skrivbord/failure.py", line 4, in fail 
    raise ValueError('Misspellled errrorr messageee') 
ValueError: Misspellled errrorr messageee 

---------------------------------------------------------------------- 
Ran 1 test in 0.001s 

FAILED (errors=1) 

ご意見はありますか? /ジョナス

編集:私はこの問題を解決するために管理ロバートRossneyからヒントを

。これは、主にスペルミスを目的としたものではなく、エラーメッセージがモジュールのユーザーにとって本当に意味のあるものであることを確認するためのものです。 unittestの通常の機能(これは私がほとんどの時間を使用する方法です)は、SHOW_ERROR_MESSAGES = Falseを設定することで実現します。

以下に示すように、単にassertRaises()メソッドをオーバーライドします。それは魅力のように機能します!

SHOW_ERROR_MESSAGES = True 

class NonexistantError(Exception): 
    pass 

class ExtendedTestCase(unittest.TestCase): 
    def assertRaises(self, excClass, callableObj, *args, **kwargs): 
     if SHOW_ERROR_MESSAGES: 
      excClass = NonexistantError 
     try: 
      unittest.TestCase.assertRaises(self, excClass, callableObj, *args, **kwargs) 
     except: 
      print '\n ' + repr(sys.exc_info()[1]) 

出力結果の割合:

testNotIntegerInput (__main__.TestCheckRegisteraddress) ... 
    TypeError('The registeraddress must be an integer. Given: 1.0',) 

    TypeError("The registeraddress must be an integer. Given: '1'",) 

    TypeError('The registeraddress must be an integer. Given: [1]',) 

    TypeError('The registeraddress must be an integer. Given: None',) 
ok 
testCorrectNumberOfBytes (__main__.TestCheckResponseNumberOfBytes) ... ok 
testInconsistentLimits (__main__.TestCheckNumerical) ... 
    ValueError('The maxvalue must not be smaller than minvalue. Given: 45 and 47, respectively.',) 

    ValueError('The maxvalue must not be smaller than minvalue. Given: 45.0 and 47.0, respectively.',) 
ok 
testWrongValues (__main__.TestCheckRegisteraddress) ... 
    ValueError('The registeraddress is too small: -1, but minimum value is 0.',) 

    ValueError('The registeraddress is too large: 65536, but maximum value is 65535.',) 
ok 
testTooShortString (__main__.TestCheckResponseWriteData) ... 
    ValueError("The payload is too short: 2, but minimum value is 4. Given: '\\x00X'",) 

    ValueError("The payload is too short: 0, but minimum value is 4. Given: ''",) 

    ValueError("The writedata is too short: 1, but minimum value is 2. Given: 'X'",) 

    ValueError("The writedata is too short: 0, but minimum value is 2. Given: ''",) 
ok 
testKnownValues (__main__.TestCreateBitPattern) ... ok 
testNotIntegerInput (__main__.TestCheckSlaveaddress) ... 
    TypeError('The slaveaddress must be an integer. Given: 1.0',) 

    TypeError("The slaveaddress must be an integer. Given: '1'",) 

    TypeError('The slaveaddress must be an integer. Given: [1]',) 

    TypeError('The slaveaddress must be an integer. Given: None',) 
ok 
+4

なぜあなたは引数をチェックする必要がある場合assertRaisesを使い続けますか?単純に例外をキャッチし、 'try'と' except'を使ってそれを調べるのはなぜですか? –

答えて

34

アウト・オブ・ボックスこれを行いませんunittest。これはあなたが頻繁に行いたいものであれば、あなたはこのような何かを試すことができます。

class ExtendedTestCase(unittest.TestCase): 

    def assertRaisesWithMessage(self, msg, func, *args, **kwargs): 
    try: 
     func(*args, **kwargs) 
     self.assertFail() 
    except Exception as inst: 
     self.assertEqual(inst.message, msg) 

ExtendedTestCase代わりの unittest.TestCaseからあなたのユニットテストクラスを派生します。

本当に、間違ったスペルのエラーメッセージが心配で、その周りにテストケースを構築したいのであれば、メッセージを文字列リテラルとしてインライン展開しないでください。他の重要な文字列を使って何をするか、それらをインポートするモジュールの定数として定義し、誰かが校正を担当するようにする必要があります。コード内の単語のスペルを間違えた開発者は、テストケースでスペルを間違えます。

+11

+1 "コード内の単語のスペルが間違っている開発者は、テストケースでスペルを間違えます。" – Johnsyweb

+7

私には、_specific_エラーが発生していることを確認するためにテストしていても、意図しない副作用のためにテストが「合格」することがあります。例えば。期待したエラーは発生しませんでしたが、同じエラータイプが別の場所で発生し、テストが満たされました。テストに合格すると、コードが盗聴されます。あなたが探しているエラーをサブクラス化するエラーについても同様です。テストがあまりにも一般的であれば、期待していないものを捕まえることになります。 –

+1

あなたは使うべき '' inst.args [0] '' 代わりの '' は、Python 2とPython 3 – oblalex

71

私はかつて@Robert Rossneyによって与えられた最も優れた答えを好んでいました。今日では、私はそうのようなコンテキストマネージャ(unittest2の新機能)としてassertRaisesを使用することを好む:あなたは、Python 2.7以降で使用可能ですassertRaisesRegexp、探している

with self.assertRaises(TypeError) as cm: 
    failure.fail() 
self.assertEqual(
    'The registeraddress must be an integer. Given: 1.0', 
    str(cm.exception) 
) 
+0

NB。 assertRaisesは、Python 2.7の 'unittest'のコンテキストマネージャとして使用できます。 unittest2はPythonの以前のバージョンの機能をバックポートします。 http://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises – powlo

+0

"with"部分でコードが失敗する場合は....私の場合は、一部で失敗する私は他のプレーンなアサーションのようにメッセージを表示したい。たとえば、self.assertEqual(cm.exception.faultCode、101001、 'フォルトコードが期待されるフォルトコード%d'と一致しない '%101001) –

+0

@arindamroychowdhury、申し訳ありませんが、かなりの期間Pythonをコード化していないので、あなたの質問に対する答えはわかりません。運が良かった。おそらく、ここの他の人の一人があなたの質問にうまく答えてくれるかもしれません。がんばろう。 – mkelley33

34

。ドキュメントから:

self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$', int, 'XYZ') 

か:

with self.assertRaisesRegexp(ValueError, 'literal'): 
    int('XYZ') 
+0

はい、予想されるエラーが発生していない場合は、メッセージは表示されません。デフォルトのメッセージは変更できません。ループの中のいくつかのパラメータをテストするときに極端に不快なこと - 関数が予想されるエラーなしで通過するパラメータがわからない。 – Mesco

4

mkelley33は、素敵な答えを与えるが、このアプローチはCodacyのようないくつかのコード分析ツールによって問題として検出することができます。問題は、assertRaisesがコンテキストマネージャとして使用できることを知らず、すべての引数がassertRaisesのメソッドに渡されないことを報告します。

だから、私はロバートのRossneyの答えを改善したいと思います:

class TestCaseMixin(object): 

    def assertRaisesWithMessage(self, exception_type, message, func, *args, **kwargs): 
     try: 
      func(*args, **kwargs) 
     except exception_type as e: 
      self.assertEqual(e.args[0], message) 
     else: 
      self.fail('"{0}" was expected to throw "{1}" exception' 
         .format(func.__name__, exception_type.__name__)) 

主な違いは以下のとおりです。

  1. 私たちは、例外の種類をテストします。
  2. Py3のエラーに message属性がないため、このコードをPython 2と Python 3(私たちはe.args[0]と呼びます)で実行できます。
8

エラーメッセージが表示したい場合は、正確なものと一致:

with self.assertRaises(ValueError) as error: 
    do_something() 
self.assertEqual(error.exception.message, 'error message') 
関連する問題