From 919db13fa8b8bbb2d49bee1c01eca0a9e7e0650a Mon Sep 17 00:00:00 2001 From: Abhijeet Kaur Date: Tue, 30 May 2017 04:36:06 +0530 Subject: [PATCH] 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. --- bots_api/bots_test_lib.py | 86 +++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/bots_api/bots_test_lib.py b/bots_api/bots_test_lib.py index e286979..e267926 100644 --- a/bots_api/bots_test_lib.py +++ b/bots_api/bots_test_lib.py @@ -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) + 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=[messages], bot_response=[bot_response], expected_method=expected_method, + http_request=http_request, http_response=http_response) 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 - # 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 - # 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) + # 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=[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,22 +73,35 @@ 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 - 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 a message via `send_message` function. - # Where response is a dictionary here. - if expected_method == "send_message": - instance.send_message.assert_called_with(response) - else: - instance.send_reply.assert_called_with(message, response['content']) + 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": + instance.send_message.assert_called_with(response) + else: + instance.send_reply.assert_called_with(message, response['content']) def bot_to_run(self, bot_module): # Returning Any, same argument as in get_bot_message_handler function.