私は、リモート サーバーに接続し、応答を待機し、その応答に基づいてアクションを実行するプロジェクトに取り組んでいます。いくつかの異なる例外をキャッチし、キャッチされた例外に応じて異なる動作をします。例:
def myMethod(address, timeout=20):
try:
response = requests.head(address, timeout=timeout)
except requests.exceptions.Timeout:
# do something special
except requests.exceptions.ConnectionError:
# do something special
except requests.exceptions.HTTPError:
# do something special
else:
if response.status_code != requests.codes.ok:
# do something special
return successfulConnection.SUCCESS
これをテストするために、次のようなテストを書きました。
class TestMyMethod(unittest.TestCase):
def test_good_connection(self):
config = {
'head.return_value': type('MockResponse', (), {'status_code': requests.codes.ok}),
'codes.ok': requests.codes.ok
}
with mock.patch('path.to.my.package.requests', **config):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.SUCCESS
)
def test_bad_connection(self):
config = {
'head.side_effect': requests.exceptions.ConnectionError,
'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError
}
with mock.patch('path.to.my.package.requests', **config):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.FAILURE
)
関数を直接実行すると、すべてが期待どおりに動作します。関数の句raise requests.exceptions.ConnectionError
を追加してテストもしましtry
た。しかし、単体テストを実行すると、
ERROR: test_bad_connection (test.test_file.TestMyMethod)
----------------------------------------------------------------
Traceback (most recent call last):
File "path/to/sourcefile", line ###, in myMethod
respone = requests.head(address, timeout=timeout)
File "path/to/unittest/mock", line 846, in __call__
return _mock_self.mock_call(*args, **kwargs)
File "path/to/unittest/mock", line 901, in _mock_call
raise effect
my.package.requests.exceptions.ConnectionError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "Path/to/my/test", line ##, in test_bad_connection
mypackage.myMethod('some_address',
File "Path/to/package", line ##, in myMethod
except requests.exceptions.ConnectionError:
TypeError: catching classes that do not inherit from BaseException is not allowed
パッチを適用していた例外を変更しようとしましたBaseException
が、ほぼ同じエラーが発生しました。
もう読んだhttps://stackoverflow.com/a/18163759/3076272すでにそうなので、どこかに悪いフックがあるに違いないと思うのです__del__
が、どこを探せばいいのか、その間に何ができるのかさえわかりません。また、私は比較的初心者なので、unittest.mock.patch()
そこでも何か間違っている可能性が非常に高いです。
これは Fusion 360 アドインなので、Fusion 360 のパッケージ版 Python 3.3 を使用しています。私の知る限り、これはバニラ バージョン (つまり、独自に作成されていない) ですが、確信はありません。
ベストアンサー1
最小限の例でエラーを再現できました:
foo.py:
class MyError(Exception):
pass
class A:
def inner(self):
err = MyError("FOO")
print(type(err))
raise err
def outer(self):
try:
self.inner()
except MyError as err:
print ("catched ", err)
return "OK"
モックなしでテストする:
class FooTest(unittest.TestCase):
def test_inner(self):
a = foo.A()
self.assertRaises(foo.MyError, a.inner)
def test_outer(self):
a = foo.A()
self.assertEquals("OK", a.outer())
はい、すべて順調です。両方のテストに合格しました
問題はモックにあります。クラス MyError がモックされるとすぐに、expect
句は何もキャッチできなくなり、質問の例と同じエラーが発生します。
class FooTest(unittest.TestCase):
def test_inner(self):
a = foo.A()
self.assertRaises(foo.MyError, a.inner)
def test_outer(self):
with unittest.mock.patch('foo.MyError'):
a = exc2.A()
self.assertEquals("OK", a.outer())
すぐに以下が与えられます:
ERROR: test_outer (__main__.FooTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "...\foo.py", line 11, in outer
self.inner()
File "...\foo.py", line 8, in inner
raise err
TypeError: exceptions must derive from BaseException
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#78>", line 8, in test_outer
File "...\foo.py", line 12, in outer
except MyError as err:
TypeError: catching classes that do not inherit from BaseException is not allowed
TypeError
ここで私はあなたが持っていなかった最初のものを手に入れました。なぜなら、あなたが'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError
設定で本当の例外を強制している間に私はモックを上げているからです。しかし、問題は残っています。このexcept
節はモックをキャッチしようとする。
TL/DR: パッケージ全体をモックするとrequests
、except requests.exceptions.ConnectionError
句はモックをキャッチしようとします。モックは実際には ではないためBaseException
、エラーが発生します。
私が思いつく唯一の解決策は、全体をモックするのではなくrequests
、例外ではない部分だけをモックすることです。モックする方法がわからなかったことを認めなければなりません。これ以外のすべてを嘲笑するしかし、あなたの例では、パッチを当てるだけで済みますrequests.head
。したがって、これは機能するはずです:
def test_bad_connection(self):
with mock.patch('path.to.my.package.requests.head',
side_effect=requests.exceptions.ConnectionError):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.FAILURE
)
つまり、head
副作用として例外のあるメソッドのみをパッチします。