对我的 python 函数的异常测试感到困惑
Confused about exception testing for my python function
我有一个函数 process_payment
,我想对其进行异常测试:
def process_payment(event, context):
try:
payDictionary= json.loads(event["body"])
if "payment_method_id" in payDictionary:
intent = stripe.PaymentIntent.create(
payment_method = payDictionary['payment_method_id'],
amount = 1000,
currency = 'usd',
payment_method_types=["card"]
)
print(intent.status)
if intent.status == 'succeeded':
return {
"statusCode": 200,
"body": json.dumps({
'message': 'Payment succeeded',
}),
}
except Exception as e:
return {
"body": json.dumps({
"message": 'Payment failed. '+ str(e),
}),
}
我想对上面的函数做异常测试,所以我用unittests框架写了下面这段代码进行测试:
import unittest
from unittest import mock
from unittest.mock import patch, Mock
import json
import stripe
from stripe.util import convert_to_stripe_object
from . import app
def test_process_payment_exception(self):
event = {
'httpMethod': 'POST',
'body': '{"payment_method_id":"pm_1HGTb2GPqNNATumTCzrTXZ9e"}'
}
response = {}
with mock.patch('stripe.PaymentIntent.create', side_effect= Exception) as mock_process_payment:
stripe_obj = convert_to_stripe_object(response)
mock_process_payment.return_value= stripe_obj
self.assertRaises(Exception, app.process_payment, event, "")
此测试代码产生以下响应:
======================================================================
FAIL: test_process_payment_exception (hello_world.test_app.TestStudent)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\...\sam-app\hello_world\test_app.py", line 524, in test_process_payment_exception
app.process_payment(event, "")
AssertionError: Exception not raised
----------------------------------------------------------------------
Ran 2 tests in 0.024s
FAILED (failures=1)
我正在努力弄清楚如何在不对我的原始代码进行额外更改的情况下对此函数进行异常测试。
编辑:
我将代码更改为如下所示:
def test_process_payment_exception(self):
event = {
'httpMethod': 'POST',
'body': '{"payment_method_id":"pm_1HGTb2GPqNNATumTCzrTXZ9e"}'
}
def mock_method():
raise Exception("someMessage")
with mock.patch('stripe.PaymentIntent.create') as mock_process_payment:
stripe_obj = convert_to_stripe_object(response)
mock_process_payment.side_effect= mock_method
ret= app.process_payment(event, "")
print(ret)
getmessage= json.loads(ret['body'])
getmessageFinal= getmessage["message"]
self.assertEqual("someMessage", getmessageFinal)
然后产生以下响应:
Traceback (most recent call last):
File "C:\Users\...\sam-app\hello_world\test_app.py", line 536, in test_process_payment_exception
....
......
+ Payment failed. mock_method() got an unexpected keyword argument 'payment_method'
-------------------- >> begin captured stdout << ---------------------
Payment failed. mock_method() got an unexpected keyword argument 'payment_method'
--------------------- >> end captured stdout << ----------------------
----------------------------------------------------------------------
Ran 2 tests in 0.024s
FAILED (failures=1)
我不明白为什么我会看到 mock_method() got an unexpected keyword argument 'payment_method'
。
你的第二个变体几乎是正确的。问题在于调用副作用时使用与原始方法相同的参数 (create
),例如它被称为:
mock_method(payment_method, amount, currency, ...)
(所有这些都是关键字参数)。由于 mock_method
没有关键字
参数已定义,您收到该错误消息(仅提及第一个缺少的参数)。
来自documentation(强调相关部分):
side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT, the return value of this function is used as the return value.
因此你必须考虑这些论点。如果您对实际参数不感兴趣,您可以只使用通用符号 *args, **kwargs
,其中 args
表示所有位置参数(作为列表),kwargs
所有关键字参数(作为字典) ).
在你的情况下这就足够了:
def test_process_payment_exception(self):
event = {
'httpMethod': 'POST',
'body': '{"payment_method_id":"pm_1HGTb2GPqNNATumTCzrTXZ9e"}'
}
def mock_method(*args, **kwargs):
raise Exception("The provided PaymentMethod ...")
with mock.patch('stripe.PaymentIntent.create') as mock_process_payment:
stripe_obj = convert_to_stripe_object(response)
...
旁注:“付款失败:”已经以调用代码为前缀,因此您不应将其放入异常中。
也就是说,在您的情况下,直接将异常分配给 side_effect
:
更容易
with mock.patch('stripe.PaymentIntent.create') as mock_process_payment:
mock_process_payment = Exception("someMessage")
stripe_obj = convert_to_stripe_object(response)
您可以直接将异常(不是异常 class)分配给 side_effect
,此外还可以分配函数(如上所示)或值列表。
我有一个函数 process_payment
,我想对其进行异常测试:
def process_payment(event, context):
try:
payDictionary= json.loads(event["body"])
if "payment_method_id" in payDictionary:
intent = stripe.PaymentIntent.create(
payment_method = payDictionary['payment_method_id'],
amount = 1000,
currency = 'usd',
payment_method_types=["card"]
)
print(intent.status)
if intent.status == 'succeeded':
return {
"statusCode": 200,
"body": json.dumps({
'message': 'Payment succeeded',
}),
}
except Exception as e:
return {
"body": json.dumps({
"message": 'Payment failed. '+ str(e),
}),
}
我想对上面的函数做异常测试,所以我用unittests框架写了下面这段代码进行测试:
import unittest
from unittest import mock
from unittest.mock import patch, Mock
import json
import stripe
from stripe.util import convert_to_stripe_object
from . import app
def test_process_payment_exception(self):
event = {
'httpMethod': 'POST',
'body': '{"payment_method_id":"pm_1HGTb2GPqNNATumTCzrTXZ9e"}'
}
response = {}
with mock.patch('stripe.PaymentIntent.create', side_effect= Exception) as mock_process_payment:
stripe_obj = convert_to_stripe_object(response)
mock_process_payment.return_value= stripe_obj
self.assertRaises(Exception, app.process_payment, event, "")
此测试代码产生以下响应:
======================================================================
FAIL: test_process_payment_exception (hello_world.test_app.TestStudent)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\...\sam-app\hello_world\test_app.py", line 524, in test_process_payment_exception
app.process_payment(event, "")
AssertionError: Exception not raised
----------------------------------------------------------------------
Ran 2 tests in 0.024s
FAILED (failures=1)
我正在努力弄清楚如何在不对我的原始代码进行额外更改的情况下对此函数进行异常测试。
编辑:
我将代码更改为如下所示:
def test_process_payment_exception(self):
event = {
'httpMethod': 'POST',
'body': '{"payment_method_id":"pm_1HGTb2GPqNNATumTCzrTXZ9e"}'
}
def mock_method():
raise Exception("someMessage")
with mock.patch('stripe.PaymentIntent.create') as mock_process_payment:
stripe_obj = convert_to_stripe_object(response)
mock_process_payment.side_effect= mock_method
ret= app.process_payment(event, "")
print(ret)
getmessage= json.loads(ret['body'])
getmessageFinal= getmessage["message"]
self.assertEqual("someMessage", getmessageFinal)
然后产生以下响应:
Traceback (most recent call last):
File "C:\Users\...\sam-app\hello_world\test_app.py", line 536, in test_process_payment_exception
....
......
+ Payment failed. mock_method() got an unexpected keyword argument 'payment_method'
-------------------- >> begin captured stdout << ---------------------
Payment failed. mock_method() got an unexpected keyword argument 'payment_method'
--------------------- >> end captured stdout << ----------------------
----------------------------------------------------------------------
Ran 2 tests in 0.024s
FAILED (failures=1)
我不明白为什么我会看到 mock_method() got an unexpected keyword argument 'payment_method'
。
你的第二个变体几乎是正确的。问题在于调用副作用时使用与原始方法相同的参数 (create
),例如它被称为:
mock_method(payment_method, amount, currency, ...)
(所有这些都是关键字参数)。由于 mock_method
没有关键字
参数已定义,您收到该错误消息(仅提及第一个缺少的参数)。
来自documentation(强调相关部分):
side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT, the return value of this function is used as the return value.
因此你必须考虑这些论点。如果您对实际参数不感兴趣,您可以只使用通用符号 *args, **kwargs
,其中 args
表示所有位置参数(作为列表),kwargs
所有关键字参数(作为字典) ).
在你的情况下这就足够了:
def test_process_payment_exception(self):
event = {
'httpMethod': 'POST',
'body': '{"payment_method_id":"pm_1HGTb2GPqNNATumTCzrTXZ9e"}'
}
def mock_method(*args, **kwargs):
raise Exception("The provided PaymentMethod ...")
with mock.patch('stripe.PaymentIntent.create') as mock_process_payment:
stripe_obj = convert_to_stripe_object(response)
...
旁注:“付款失败:”已经以调用代码为前缀,因此您不应将其放入异常中。
也就是说,在您的情况下,直接将异常分配给 side_effect
:
with mock.patch('stripe.PaymentIntent.create') as mock_process_payment:
mock_process_payment = Exception("someMessage")
stripe_obj = convert_to_stripe_object(response)
您可以直接将异常(不是异常 class)分配给 side_effect
,此外还可以分配函数(如上所示)或值列表。