Python 如何测试Tornado处理程序是否正确调用了另一个API?
我的Tornado应用程序有一个处理程序,它使用Python 如何测试Tornado处理程序是否正确调用了另一个API?,python,tornado,Python,Tornado,我的Tornado应用程序有一个处理程序,它使用AsyncHTTPClient调用另一个外部API,然后返回 我如何编写一个单元测试来测试我的处理程序是否正确调用了外部API 我觉得我应该在我的单元测试中运行第二台服务器,它模拟外部服务器并提供与我的处理程序所使用的相同的API。我可以将要命中的URI作为参数提供给我的应用程序,因此它不是硬编码的。但是,我不确定如何在AsyncHTTPTestCase中(正确地)启动2台服务器。关于如何分解和正确处理测试,有很多意见。对于我测试必须与第三方对话的
AsyncHTTPClient
调用另一个外部API,然后返回
我如何编写一个单元测试来测试我的处理程序是否正确调用了外部API
我觉得我应该在我的单元测试中运行第二台服务器,它模拟外部服务器并提供与我的处理程序所使用的相同的API。我可以将要命中的URI作为参数提供给我的应用程序,因此它不是硬编码的。但是,我不确定如何在
AsyncHTTPTestCase
中(正确地)启动2台服务器。关于如何分解和正确处理测试,有很多意见。对于我测试必须与第三方对话的内容的场景,在您的示例中,我将其分为3个部分,一个非常薄的部分,只与API对话。然后,我将编写一个集成测试来测试与API对话的部分。然后,我将制作一个模拟或其他测试装置,用于替换对API的调用,并编写处理API调用的代码。这可以很容易地放在单元测试中,因为它通常类似于:
my_new_data = do_process(call_my_third_party_api())
只需传入第三方api调用返回的数据的模拟,就可以轻松地为do_进程编写测试
然后,当测试处理程序时,您只需使用返回您希望从第三方获得的结果的东西模拟api调用
现在您有三个不同的测试来测试应用程序的每个区域。可以运行的测试,以确保代码正确访问API。告诉您是否正确处理API处理的测试,以及告诉您是否正确将该信息返回给最终用户的第三个测试。您可以使用模拟库(python3.5中的unittest.mock):
我也有同样的问题 这是我在python3.5中的解决方案 handler.py
class VerificationCodeHandler(BaseRequestHandler):
@asynchronous
@gen.coroutine
def post(self):
code_type = self.body_argument.get('code_type', 'register')
phone_number = self.body_argument.get('phone_number')
# 发送阿里云短信
try:
# This is another API, encapsulate into a coroutine
send_result = yield ali_sms.ali_send_code_sms(phone_number, code_type, code)
http_code, send_sms_result = send_result.code, json.loads(send_result.body.decode('utf-8'))
if int(http_code) == 200:
if send_sms_result.get('Code').upper() == 'OK':
self.success(self.prompt_msg.AUTH_SEND_MSG_SUCCESS)
# 缓存数据
self.redis.set(key_lasttime, 'send', ex=settings.SMS_INTERVAL)
self.redis.set(key_times,
int(is_overrun) + 1 if is_overrun else 1,
ex=settings.SMS_USER_LIMIT)
self.redis.set(key_code, code, ex=settings.SMS_EXPIRATION)
else:
self.fail(send_sms_result.get('Message', self.prompt_msg.AUTH_SEND_MSG_ERROR))
else:
self.fail(self.prompt_msg.AUTH_SEND_MSG_ERROR_AGAIN)
except:
# TODO 系统异常,需要通知管理员
logger.exception('发送短信失败')
self.fail(self.prompt_msg.AUTH_SEND_MSG_ERROR_AGAIN)
ali_sms.py
async def ali_send_code_sms(phone_number, code_type, code):
url = get_aliyun_sms_url(phone_number, code_type, code)
http_client = AsyncHTTPClient()
resp = await http_client.fetch(url)
return resp
unittest.mock
class VerificationCodeTestCast(BaseHandelrTestCase):
@mock.patch('common.aliyun.sms.ali_send_code_sms')
def test_send_sms_fail(self, sms):
fetch_future = tornado.concurrent.Future()
fetch_future._result = mock.Mock(body=json_encode({'Code': 'OK'}).encode('utf-8'), code=200)
fetch_future._done = True
sms.return_value = fetch_future
resp = self.fetch('/get_ver_code/', method='POST',
body=json_encode({'code_type': 'register',
'phone_number': '17980888160'})
)
result = json_decode(resp.body)
self.assertEqual(200, resp.code)
self.assertEqual(400, result.get('code'))
self.assertEqual(result['msg'], '今日发送短信超限,请明日再试')
注:
在处理程序中使用
ali\u-send\u-code\u-sms
时,不要从ali\u-sms导入ali\u-send\u-code\u-sms,import-ali\u-sms
然后ali\u-send\u-code\u-sms
,如果我理解正确,您想测试asynchtpclient
是否按预期工作?我想龙卷风的单元测试已经涵盖了这一点。如果你想测试你的呼叫是否成功,你真的不能;即使您有一个测试服务并测试调用它,它也与实际服务的工作方式毫无关系(换言之:您的测试可能通过,但真正的服务可能失败)。您应该专注于测试您引入的功能,例如处理服务的响应(可以模拟),而不是试图覆盖您无法控制的内容。@BerislavLopac感谢您的评论。我不是要测试AsyncHTTPClient
,而是测试我的处理程序是否创建了正确的主体,是否正确调用了外部API,是否正确处理了响应。是的,我不控制外部API,但我可以控制如何与它交互,这就是我要测试的。我明白你的意思。然而,这看起来像是三个独立的职责,理想情况下应该是在不同的方法中。测试身体创建和响应处理程序。这实际上是我最后做的。这不是一个完整的端到端测试,如果不使用实际的外部API,这甚至是不可能的,但至少有更少的错误空间。
class VerificationCodeTestCast(BaseHandelrTestCase):
@mock.patch('common.aliyun.sms.ali_send_code_sms')
def test_send_sms_fail(self, sms):
fetch_future = tornado.concurrent.Future()
fetch_future._result = mock.Mock(body=json_encode({'Code': 'OK'}).encode('utf-8'), code=200)
fetch_future._done = True
sms.return_value = fetch_future
resp = self.fetch('/get_ver_code/', method='POST',
body=json_encode({'code_type': 'register',
'phone_number': '17980888160'})
)
result = json_decode(resp.body)
self.assertEqual(200, resp.code)
self.assertEqual(400, result.get('code'))
self.assertEqual(result['msg'], '今日发送短信超限,请明日再试')