bots: Add mock test support for bots using internet services.

Mock requests.get function for bots relying on external services
which may return different results. 'http_response' is mocked to
check if the bots run well and to make the tests determinitic. The
code first checks if the http request corresponding to the http response
is called or not.

This commit renders test files of bots using the internet
(third party) services to Fail. As requests.get() is replaced
by our mock function.

Since we have not provided mock http response for bots like xkcd,
wikipedia, define etc. Their requests.get() function returns empty
message.
This commit is contained in:
Abhijeet Kaur 2017-05-30 04:36:06 +05:30 committed by Tim Abbott
parent 72ddcc1902
commit 919db13fa8

View file

@ -7,6 +7,8 @@ import os
import sys
import unittest
import logging
import requests
import mock
from mock import MagicMock, patch
@ -17,7 +19,7 @@ from six.moves import zip
from unittest import TestCase
from typing import List, Dict, Any
from typing import List, Dict, Any, Optional
from types import ModuleType
current_dir = os.path.dirname(os.path.abspath(__file__))
@ -25,8 +27,10 @@ current_dir = os.path.dirname(os.path.abspath(__file__))
class BotTestCase(TestCase):
bot_name = '' # type: str
def check_expected_responses(self, expectations, expected_method='send_reply', email="foo_sender@zulip.com", recipient="foo", subject="foo", type="all"):
# type: (Dict[str, Any], str, str, str, str, str) -> None
def check_expected_responses(self, expectations, expected_method='send_reply',
email="foo_sender@zulip.com", recipient="foo", subject="foo",
type="all", http_request=None, http_response=None):
# type: (Dict[str, Any], str, str, str, str, str, Dict[str, Any], Dict[str, Any]) -> None
# To test send_message, Any would be a Dict type,
# to test send_reply, Any would be a str type.
if type not in ["private", "stream", "all"]:
@ -34,32 +38,29 @@ class BotTestCase(TestCase):
for m, r in expectations.items():
if type != "stream":
self.mock_test(
{'content': m, 'type': "private", 'display_recipient': recipient,
'sender_email': email}, r, expected_method)
messages={'content': m, 'type': "private", 'display_recipient': recipient,
'sender_email': email}, bot_response=r, expected_method=expected_method,
http_request=http_request, http_response=http_response)
if type != "private":
self.mock_test(
{'content': m, 'type': "stream", 'display_recipient': recipient,
'subject': subject, 'sender_email': email}, r, expected_method)
messages={'content': m, 'type': "stream", 'display_recipient': recipient,
'subject': subject, 'sender_email': email}, bot_response=r,
expected_method=expected_method, http_request=http_request, http_response=http_response)
def mock_test(self, messages, bot_response, expected_method):
# type: (Dict[str, str], Any, str) -> None
if expected_method == "send_reply":
self.mock_test_send_reply(messages, bot_response, expected_method)
else:
self.mock_test_send_message(messages, bot_response, expected_method)
def mock_test_send_message(self, messages, bot_response, expected_method):
# type: (Dict[str, str], Dict[str, str], str) -> None
def mock_test(self, messages, bot_response, expected_method,
http_request=None, http_response=None):
# type: (Dict[str, str], Any, str, Dict[str, Any], Dict[str, Any]) -> None
if expected_method == "send_message":
# Since send_message function uses bot_response of type Dict, no
# further changes required.
self.assert_bot_output([messages], [bot_response], expected_method)
def mock_test_send_reply(self, messages, bot_response, expected_method):
# type: (Dict[str, str], str, str) -> None
self.assert_bot_output(messages=[messages], bot_response=[bot_response], expected_method=expected_method,
http_request=http_request, http_response=http_response)
else:
# Since send_reply function uses bot_response of type str, we
# do convert the str type to a Dict type to have the same assert_bot_output function.
bot_response_type_dict = {'content': bot_response}
self.assert_bot_output([messages], [bot_response_type_dict], expected_method)
self.assert_bot_output(messages=[messages], bot_response=[bot_response_type_dict], expected_method=expected_method,
http_request=http_request, http_response=http_response)
def get_bot_message_handler(self):
# type: () -> Any
@ -72,16 +73,29 @@ class BotTestCase(TestCase):
message_handler = self.bot_to_run(bot_module)
return message_handler
def assert_bot_output(self, messages, bot_response, expected_method):
# type: (List[Dict[str, Any]], List[Dict[str, str]], str) -> None
def assert_bot_output(self, messages, bot_response, expected_method,
http_request=None, http_response=None):
# type: (List[Dict[str, Any]], List[Dict[str, str]], str, Optional[Dict[str, Any]], Optional[Dict[str, Any]]) -> None
message_handler = self.get_bot_message_handler()
# Mocking BotHandlerApi
with patch('bots_api.bot_lib.BotHandlerApi') as MockClass:
instance = MockClass.return_value
with patch('requests.get') as mock_get:
mock_result = mock.MagicMock()
mock_result.json.return_value = http_response
mock_result.ok.return_value = True
mock_get.return_value = mock_result
for (message, response) in zip(messages, bot_response):
# Send message to the concerned bot
message_handler.handle_message(message, MockClass(), StateHandler())
# Check if the bot is sending the correct http_request corresponding
# to the given http_response.
if http_request is not None:
mock_get.assert_called_with(http_request['api_url'], params=http_request['params'])
# Check if the bot is sending a message via `send_message` function.
# Where response is a dictionary here.
if expected_method == "send_message":