即使在使用 "http.client" Python 时引发异常,AssertRaises 也会失败
AssertRaises fails even exception is raised when using "http.client" Python
我正在为使用库 http.Client.
的项目创建 API class
问题出在单元测试上,当我尝试引发错误并使用 assertRaises 对其进行测试时,即使引发了正确的异常,assertRaises 也会失败。
我有一个模块'Foo'。
+---Foo
¦ __init__.py
¦ api.py
我的 __init__.py 里面只有我的例外 classes 和装饰器
import errno
import json
import os
import signal
from dataclasses import dataclass
from functools import wraps
from http.client import HTTPMessage, HTTPException
from typing import Optional
@dataclass
class APIResponse:
method: str
code: int
reason: str
length: Optional[int]
headers: HTTPMessage
message: HTTPMessage
@dataclass
class Token:
access_token: str
expires_in: int
token_type: str
@dataclass
class ClientCredentials:
client_id: str
client_secret: str
audience: str
grant_type: str = "client_credentials"
class ClientCredentialsEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, ClientCredentials):
return o.__dict__
return json.JSONEncoder.default(self, o)
class AuthenticationError(HTTPException):
pass
def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
def decorator(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wraps(func)(wrapper)
return decorator
我的 api.py 包含我的 API class 我正在测试获取访问令牌的身份验证方法
import http.client
import json
import logging
from Foo import ClientCredentials, Token, ClientCredentialsEncoder, AuthenticationError, timeout
from settings import config, API_HOST, AUTH_DOMAIN
class API:
def __init__(self, auth0_domain, client_credentials: ClientCredentials):
self.auth0_domain: str = auth0_domain
self.client_credentials = client_credentials
self._session = http.client.HTTPSConnection(self.auth0_domain)
self.token: Token = self.authenticate()
@property
def session(self):
return self._session
@timeout(10, "API Takes too long to respond.")
def api_request(self, method: str, url: str, body: str, headers: dict):
logging.debug(f"Senging {method} request to {url} for data: {body}...")
self.session.request(method=method, url=url, body=body, headers=headers)
res = self.session.getresponse()
return res
def authenticate(self) -> Token:
logging.debug("Getting API authentiation...")
method = "POST"
url = "/oauth/token"
body = json.dumps(
obj=self.client_credentials,
cls=ClientCredentialsEncoder
)
headers = {'content-type': "application/json"}
response = self.api_request(method=method, url=url, body=body, headers=headers)
status = response.status
reason = response.reason
data = response.read()
json_dict = json.loads(data)
self._session.close()
if status == 403:
logging.error(f"Error {status}: {reason}. Authentication fail.")
raise AuthenticationError(
f"Invalid Credentials. Error: '{json_dict['error']}', Description: {json_dict['error_description']}"
)
if status == 401:
logging.error(f"Error {status}: {reason}. Authentication fail.")
raise AuthenticationError(
f"Invalid Credentials. Error: '{json_dict['error']}', Description: {json_dict['error_description']}"
)
logging.info(f"Response {status}. Authentication successful!")
return Token(
access_token=json_dict['access_token'],
expires_in=json_dict['expires_in'],
token_type=json_dict['token_type'],
)
现在我正在尝试测试我的身份验证是否在获得无效凭据时引发正确的错误。
这是我的测试脚本
import logging
import unittest
from typing import Tuple
from Foo import ClientCredentials, Token, AuthenticationError
from Foo.api import API
from settings import config, API_HOST
class MyTestCase(unittest.TestCase):
def setUp(self):
pass
def test_token(self):
api = API(*self.good_credentials)
self.assertIsInstance(api.authenticate(), Token)
def test_invalid_credentials(self):
api = API(*self.invalid_credentials)
# self.assertRaises(AuthenticationError, api.authenticate)
with self.assertRaises(AuthenticationError) as error_msg:
api.authenticate()
self.assertEqual(error_msg, "Invalid Credentials. Error: 'access_denied', Description: Unauthorized")
@unittest.skip("Can't get the right assertRaises")
def test_invalid_auth_domain(self):
api = API(*self.invalid_auth_domain)
with self.assertRaises(TimeoutError) as e:
api.authenticate()
self.assertEqual(e, "API Takes too long to respond.")
# self.assertRaises(TimeoutError, api2.authenticate())
@property
def good_credentials(self) -> Tuple[str, ClientCredentials]:
auth0_domain = "my.auth.domain"
credentials = ClientCredentials(
client_id=config.read_param('api.value.client-id'),
# client_id='aw;oieja;wlejf',
client_secret=config.read_param('api.value.client-secret'),
audience=API_HOST,
)
return auth0_domain, credentials
@property
def invalid_auth_domain(self) -> Tuple[str, ClientCredentials]:
auth0_domain = "my.auth.domain"
credentials = ClientCredentials(
client_id=config.read_param('api.value.client-id'),
client_secret=config.read_param('api.value.client-secret'),
audience=API_HOST,
)
return auth0_domain, credentials
@property
def invalid_credentials(self) -> Tuple[str, ClientCredentials]:
auth0_domain = "my.auth.domain"
credentials = ClientCredentials(
# client_id=config.read_param('api.value.client-id'),
client_id='aw;oieja;wlejf',
client_secret=config.read_param('api.value.client-secret'),
audience=API_HOST,
)
return auth0_domain, credentials
if __name__ == '__main__':
unittest.main()
正如您在我的测试中看到的那样,我已经尝试了两种方法用于 assertRaises:
# Trial 1
with self.assertRaises(AuthenticationError) as e:
api.authenticate()
self.assertEqual(error_msg, "Invalid Credentials. Error: 'access_denied', Description: Unauthorized")
# Trial 2
self.assertRaises(AuthenticationError, api.authenticate)
即使引发了正确的异常,assertRaises 也会失败。
这是我在 运行 单元测试后从终端获得的日志:
Ran 3 tests in 0.842s
FAILED (errors=1, skipped=1)
Error
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 59, in testPartExecutor
yield
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 615, in run
testMethod()
File "/Users/pluggle/Documents/Gitlab/Laveness/onboarding/test/api_authentication_test.py", line 22, in test_invalid_credentials
api = API(*self.invalid_credentials)
File "/Users/pluggle/Documents/Gitlab/Laveness/onboarding/Foo/api.py", line 17, in __init__
self.token: Token = self.authenticate()
File "/Users/pluggle/Documents/Gitlab/Laveness/onboarding/Foo/api.py", line 66, in authenticate
f"Invalid Credentials. Error: '{json_dict['error']}', Description: {json_dict['error_description']}"
Foo.AuthenticationError: Invalid Credentials. Error: 'access_denied', Description: Unauthorized
Assertion failed
Assertion failed
Process finished with exit code 1
Assertion failed
Assertion failed
来自这个问题:AssertRaises fails even though exception is raised
他似乎和我有同样的问题,但他从 2 个不同的路径导入错误,其中对我来说,我很确定我的异常是从我的 __init__.py 在我的 Foo 包上。
真的希望有人能帮助我。我真的觉得我在这里忽略了一些东西。
非常感谢!
如果您阅读堆栈跟踪,它会在这一行引发:
api = API(*self.invalid_credentials)
在 assertRaises
之前的一行,因此不会被它捕获
这是因为API.__init__
本身调用了self.authenticate()
我正在为使用库 http.Client.
的项目创建 API class问题出在单元测试上,当我尝试引发错误并使用 assertRaises 对其进行测试时,即使引发了正确的异常,assertRaises 也会失败。
我有一个模块'Foo'。
+---Foo
¦ __init__.py
¦ api.py
我的 __init__.py 里面只有我的例外 classes 和装饰器
import errno
import json
import os
import signal
from dataclasses import dataclass
from functools import wraps
from http.client import HTTPMessage, HTTPException
from typing import Optional
@dataclass
class APIResponse:
method: str
code: int
reason: str
length: Optional[int]
headers: HTTPMessage
message: HTTPMessage
@dataclass
class Token:
access_token: str
expires_in: int
token_type: str
@dataclass
class ClientCredentials:
client_id: str
client_secret: str
audience: str
grant_type: str = "client_credentials"
class ClientCredentialsEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, ClientCredentials):
return o.__dict__
return json.JSONEncoder.default(self, o)
class AuthenticationError(HTTPException):
pass
def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
def decorator(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wraps(func)(wrapper)
return decorator
我的 api.py 包含我的 API class 我正在测试获取访问令牌的身份验证方法
import http.client
import json
import logging
from Foo import ClientCredentials, Token, ClientCredentialsEncoder, AuthenticationError, timeout
from settings import config, API_HOST, AUTH_DOMAIN
class API:
def __init__(self, auth0_domain, client_credentials: ClientCredentials):
self.auth0_domain: str = auth0_domain
self.client_credentials = client_credentials
self._session = http.client.HTTPSConnection(self.auth0_domain)
self.token: Token = self.authenticate()
@property
def session(self):
return self._session
@timeout(10, "API Takes too long to respond.")
def api_request(self, method: str, url: str, body: str, headers: dict):
logging.debug(f"Senging {method} request to {url} for data: {body}...")
self.session.request(method=method, url=url, body=body, headers=headers)
res = self.session.getresponse()
return res
def authenticate(self) -> Token:
logging.debug("Getting API authentiation...")
method = "POST"
url = "/oauth/token"
body = json.dumps(
obj=self.client_credentials,
cls=ClientCredentialsEncoder
)
headers = {'content-type': "application/json"}
response = self.api_request(method=method, url=url, body=body, headers=headers)
status = response.status
reason = response.reason
data = response.read()
json_dict = json.loads(data)
self._session.close()
if status == 403:
logging.error(f"Error {status}: {reason}. Authentication fail.")
raise AuthenticationError(
f"Invalid Credentials. Error: '{json_dict['error']}', Description: {json_dict['error_description']}"
)
if status == 401:
logging.error(f"Error {status}: {reason}. Authentication fail.")
raise AuthenticationError(
f"Invalid Credentials. Error: '{json_dict['error']}', Description: {json_dict['error_description']}"
)
logging.info(f"Response {status}. Authentication successful!")
return Token(
access_token=json_dict['access_token'],
expires_in=json_dict['expires_in'],
token_type=json_dict['token_type'],
)
现在我正在尝试测试我的身份验证是否在获得无效凭据时引发正确的错误。 这是我的测试脚本
import logging
import unittest
from typing import Tuple
from Foo import ClientCredentials, Token, AuthenticationError
from Foo.api import API
from settings import config, API_HOST
class MyTestCase(unittest.TestCase):
def setUp(self):
pass
def test_token(self):
api = API(*self.good_credentials)
self.assertIsInstance(api.authenticate(), Token)
def test_invalid_credentials(self):
api = API(*self.invalid_credentials)
# self.assertRaises(AuthenticationError, api.authenticate)
with self.assertRaises(AuthenticationError) as error_msg:
api.authenticate()
self.assertEqual(error_msg, "Invalid Credentials. Error: 'access_denied', Description: Unauthorized")
@unittest.skip("Can't get the right assertRaises")
def test_invalid_auth_domain(self):
api = API(*self.invalid_auth_domain)
with self.assertRaises(TimeoutError) as e:
api.authenticate()
self.assertEqual(e, "API Takes too long to respond.")
# self.assertRaises(TimeoutError, api2.authenticate())
@property
def good_credentials(self) -> Tuple[str, ClientCredentials]:
auth0_domain = "my.auth.domain"
credentials = ClientCredentials(
client_id=config.read_param('api.value.client-id'),
# client_id='aw;oieja;wlejf',
client_secret=config.read_param('api.value.client-secret'),
audience=API_HOST,
)
return auth0_domain, credentials
@property
def invalid_auth_domain(self) -> Tuple[str, ClientCredentials]:
auth0_domain = "my.auth.domain"
credentials = ClientCredentials(
client_id=config.read_param('api.value.client-id'),
client_secret=config.read_param('api.value.client-secret'),
audience=API_HOST,
)
return auth0_domain, credentials
@property
def invalid_credentials(self) -> Tuple[str, ClientCredentials]:
auth0_domain = "my.auth.domain"
credentials = ClientCredentials(
# client_id=config.read_param('api.value.client-id'),
client_id='aw;oieja;wlejf',
client_secret=config.read_param('api.value.client-secret'),
audience=API_HOST,
)
return auth0_domain, credentials
if __name__ == '__main__':
unittest.main()
正如您在我的测试中看到的那样,我已经尝试了两种方法用于 assertRaises:
# Trial 1
with self.assertRaises(AuthenticationError) as e:
api.authenticate()
self.assertEqual(error_msg, "Invalid Credentials. Error: 'access_denied', Description: Unauthorized")
# Trial 2
self.assertRaises(AuthenticationError, api.authenticate)
即使引发了正确的异常,assertRaises 也会失败。 这是我在 运行 单元测试后从终端获得的日志:
Ran 3 tests in 0.842s
FAILED (errors=1, skipped=1)
Error
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 59, in testPartExecutor
yield
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 615, in run
testMethod()
File "/Users/pluggle/Documents/Gitlab/Laveness/onboarding/test/api_authentication_test.py", line 22, in test_invalid_credentials
api = API(*self.invalid_credentials)
File "/Users/pluggle/Documents/Gitlab/Laveness/onboarding/Foo/api.py", line 17, in __init__
self.token: Token = self.authenticate()
File "/Users/pluggle/Documents/Gitlab/Laveness/onboarding/Foo/api.py", line 66, in authenticate
f"Invalid Credentials. Error: '{json_dict['error']}', Description: {json_dict['error_description']}"
Foo.AuthenticationError: Invalid Credentials. Error: 'access_denied', Description: Unauthorized
Assertion failed
Assertion failed
Process finished with exit code 1
Assertion failed
Assertion failed
来自这个问题:AssertRaises fails even though exception is raised
他似乎和我有同样的问题,但他从 2 个不同的路径导入错误,其中对我来说,我很确定我的异常是从我的 __init__.py 在我的 Foo 包上。
真的希望有人能帮助我。我真的觉得我在这里忽略了一些东西。
非常感谢!
如果您阅读堆栈跟踪,它会在这一行引发:
api = API(*self.invalid_credentials)
在 assertRaises
之前的一行,因此不会被它捕获
这是因为API.__init__
本身调用了self.authenticate()