我正在为使用库 http.Client 的项目创建一个 API 类。
问题在于单元测试,当我尝试引发错误并使用 assertRaises 对其进行测试时,即使引发了正确的异常,assertRaises 也会失败。
我有一个模块'Foo'。
+---Foo
¦ __init__.py
¦ api.py
在我的__init__.py里面只有我的异常类和装饰器
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 类
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 也会失败
似乎他和我有同样的问题,但他从 2 个不同的路径导入错误,其中对我来说,我很确定我的异常是从我的 Foo 包上的__init__.py导入的。
真的希望有人能帮助我。我真的觉得我在这里忽略了一些东西。
非常感谢!