bot tests: Adjust TestCase inheritance to avoid need to skip/filter.

Previously the test-bots script filtered out base-class tests from
BotTestCase. With this change, BotTestCase continues to inherit from
unittest.TestCase, but the default test_* methods previously in this
class are now in a new DefaultTests class, which does not. Instead, each
bot needs to inherit from BotTestCase and DefaultTests *explicitly*.

This avoids the need to filter out the base-class tests, which
simplifies the test-bots script, and may ease any migration to eg.
pytest.

The DefaultTests class does require some non-implemented methods which
BotTestCase provides.
This commit is contained in:
neiljp (Neil Pilgrim) 2018-06-08 14:30:50 -07:00 committed by showell
parent c636a5ac49
commit 6cdb83ce72
43 changed files with 105 additions and 102 deletions

View file

@ -95,19 +95,6 @@ def main():
if options.error_on_no_init:
sys.exit(1)
def filter_tests(tests):
# type: (Union[TestSuite, TestCase]) -> TestSuite
filtered_tests = TestSuite()
for test in tests:
if isinstance(test, TestCase):
# Exclude test base class from being tested.
if test.__class__.__name__ != 'BotTestCase':
filtered_tests.addTest(test)
else:
filtered_tests.addTest(filter_tests(test))
return filtered_tests
test_suites = filter_tests(test_suites)
suite = unittest.TestSuite(test_suites)
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)

View file

@ -1,9 +1,9 @@
from unittest.mock import patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.test_lib import StubBotHandler
from zulip_bots.bots.baremetrics.baremetrics import BaremetricsHandler
class TestBaremetricsBot(BotTestCase):
class TestBaremetricsBot(BotTestCase, DefaultTests):
bot_name = "baremetrics"
def test_bot_responds_to_empty_message(self) -> None:

View file

@ -1,8 +1,8 @@
from unittest.mock import patch, Mock
from zulip_bots.test_lib import StubBotHandler, BotTestCase, get_bot_message_handler
from zulip_bots.test_lib import StubBotHandler, BotTestCase, DefaultTests, get_bot_message_handler
from requests.exceptions import ConnectionError
class TestBeeminderBot(BotTestCase):
class TestBeeminderBot(BotTestCase, DefaultTests):
bot_name = "beeminder"
normal_config = {
"auth_token": "XXXXXX",

View file

@ -1,6 +1,6 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
class TestChessBot(BotTestCase):
class TestChessBot(BotTestCase, DefaultTests):
bot_name = "chessbot"
START_RESPONSE = '''New game! The board looks like this:

View file

@ -1,4 +1,4 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from contextlib import contextmanager
from unittest.mock import MagicMock
@ -7,7 +7,7 @@ from zulip_bots.game_handler import BadMoveException
from typing import Dict, Any, List
class TestConnectFourBot(BotTestCase):
class TestConnectFourBot(BotTestCase, DefaultTests):
bot_name = 'connect_four'
def make_request_message(

View file

@ -1,8 +1,8 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.bots.converter import utils
class TestConverterBot(BotTestCase):
class TestConverterBot(BotTestCase, DefaultTests):
bot_name = "converter"
def test_bot(self) -> None:

View file

@ -1,7 +1,7 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from unittest.mock import patch
class TestDefineBot(BotTestCase):
class TestDefineBot(BotTestCase, DefaultTests):
bot_name = "define"
def test_bot(self) -> None:

View file

@ -1,4 +1,4 @@
from zulip_bots.test_lib import BotTestCase, read_bot_fixture_data
from zulip_bots.test_lib import BotTestCase, DefaultTests, read_bot_fixture_data
from contextlib import contextmanager
@ -40,7 +40,7 @@ def mock_dialogflow(test_name: str, bot_name: str) -> Any:
mock_text_request.return_value = request
yield
class TestDialogFlowBot(BotTestCase):
class TestDialogFlowBot(BotTestCase, DefaultTests):
bot_name = 'dialogflow'
def _test(self, test_name: str, message: str, response: str) -> None:

View file

@ -1,4 +1,4 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from typing import List
from unittest.mock import patch
@ -73,7 +73,7 @@ def get_help() -> str:
```
'''
class TestDropboxBot(BotTestCase):
class TestDropboxBot(BotTestCase, DefaultTests):
bot_name = "dropbox_share"
config_info = {"access_token": "1234567890"}

View file

@ -1,6 +1,6 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
class TestEncryptBot(BotTestCase):
class TestEncryptBot(BotTestCase, DefaultTests):
bot_name = "encrypt"
def test_bot(self) -> None:

View file

@ -1,8 +1,8 @@
from unittest.mock import patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from requests.exceptions import ConnectionError
class TestFlockBot(BotTestCase):
class TestFlockBot(BotTestCase, DefaultTests):
bot_name = "flock"
normal_config = {"token": "12345"}

View file

@ -1,13 +1,14 @@
from zulip_bots.test_lib import (
StubBotHandler,
BotTestCase,
DefaultTests,
get_bot_message_handler,
)
from typing import Any
class TestFollowUpBot(BotTestCase):
class TestFollowUpBot(BotTestCase, DefaultTests):
bot_name = "followup"
def test_followup_stream(self) -> None:

View file

@ -1,8 +1,8 @@
from typing import Any, Dict
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
class TestFrontBot(BotTestCase):
class TestFrontBot(BotTestCase, DefaultTests):
bot_name = 'front'
def make_request_message(self, content: str) -> Dict[str, Any]:
@ -49,7 +49,7 @@ class TestFrontBot(BotTestCase):
with self.mock_http_conversation('comment'):
self.verify_reply("comment " + body, "Comment was sent.")
class TestFrontBotWrongTopic(BotTestCase):
class TestFrontBotWrongTopic(BotTestCase, DefaultTests):
bot_name = 'front'
def make_request_message(self, content: str) -> Dict[str, Any]:

View file

@ -1,4 +1,4 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.game_handler import GameInstance
from contextlib import contextmanager
@ -7,7 +7,7 @@ from mock import MagicMock, patch
from typing import Any, Dict, List
class TestGameHandlerBot(BotTestCase):
class TestGameHandlerBot(BotTestCase, DefaultTests):
bot_name = 'game_handler_bot'
def make_request_message(

View file

@ -1,4 +1,4 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from contextlib import contextmanager
from unittest.mock import MagicMock
@ -7,7 +7,7 @@ from zulip_bots.game_handler import BadMoveException
from typing import Dict, Any, List, Tuple
class TestGameOfFifteenBot(BotTestCase):
class TestGameOfFifteenBot(BotTestCase, DefaultTests):
bot_name = 'game_of_fifteen'
def make_request_message(

View file

@ -2,9 +2,9 @@ from unittest.mock import patch, MagicMock
from requests.exceptions import HTTPError, ConnectionError
from typing import Any, Union
from zulip_bots.test_lib import StubBotHandler, BotTestCase, get_bot_message_handler
from zulip_bots.test_lib import StubBotHandler, BotTestCase, DefaultTests, get_bot_message_handler
class TestGiphyBot(BotTestCase):
class TestGiphyBot(BotTestCase, DefaultTests):
bot_name = "giphy"
# Test for bot response to empty message

View file

@ -1,12 +1,13 @@
from zulip_bots.test_lib import (
StubBotHandler,
BotTestCase,
DefaultTests,
get_bot_message_handler,
)
from typing import Any
class TestGithubDetailBot(BotTestCase):
class TestGithubDetailBot(BotTestCase, DefaultTests):
bot_name = "github_detail"
mock_config = {'owner': 'zulip', 'repo': 'zulip'}
empty_config = {'owner': '', 'repo': ''}

View file

@ -1,8 +1,8 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from unittest.mock import patch
class TestGoogleSearchBot(BotTestCase):
class TestGoogleSearchBot(BotTestCase, DefaultTests):
bot_name = 'google_search'
# Simple query

View file

@ -1,7 +1,7 @@
from unittest.mock import patch
from requests.exceptions import ConnectionError
from zulip_bots.test_lib import BotTestCase, StubBotHandler
from zulip_bots.test_lib import BotTestCase, DefaultTests, StubBotHandler
from zulip_bots.bots.google_translate.google_translate import TranslateError
help_text = '''
@ -11,7 +11,7 @@ Please format your message like:
Visit [here](https://cloud.google.com/translate/docs/languages) for all languages
'''
class TestGoogleTranslateBot(BotTestCase):
class TestGoogleTranslateBot(BotTestCase, DefaultTests):
bot_name = "google_translate"
def _test(self, message, response, http_config_fixture, http_fixture=None):

View file

@ -1,6 +1,6 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
class TestHelpBot(BotTestCase):
class TestHelpBot(BotTestCase, DefaultTests):
bot_name = "helloworld" # type: str
def test_bot(self) -> None:

View file

@ -1,8 +1,8 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from typing import Any
class TestHelpBot(BotTestCase):
class TestHelpBot(BotTestCase, DefaultTests):
bot_name = "help"
def test_bot(self) -> None:

View file

@ -1,10 +1,10 @@
from unittest.mock import patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
import requests
class TestIDoneThisBot(BotTestCase):
class TestIDoneThisBot(BotTestCase, DefaultTests):
bot_name = "idonethis" # type: str
def test_create_entry_default_team(self) -> None:

View file

@ -3,10 +3,11 @@ from unittest import mock
from zulip_bots.test_lib import (
get_bot_message_handler,
StubBotHandler,
DefaultTests,
BotTestCase,
)
class TestIncrementorBot(BotTestCase):
class TestIncrementorBot(BotTestCase, DefaultTests):
bot_name = "incrementor"
def test_bot(self) -> None:

View file

@ -1,7 +1,7 @@
from unittest.mock import patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
class TestJiraBot(BotTestCase):
class TestJiraBot(BotTestCase, DefaultTests):
bot_name = 'jira'
MOCK_CONFIG_INFO = {

View file

@ -1,10 +1,10 @@
from unittest.mock import patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.test_lib import StubBotHandler
from zulip_bots.bots.link_shortener.link_shortener import LinkShortenerHandler
class TestLinkShortenerBot(BotTestCase):
class TestLinkShortenerBot(BotTestCase, DefaultTests):
bot_name = "link_shortener"
def _test(self, message: str, response: str) -> None:

View file

@ -1,9 +1,9 @@
from zulip_bots.bots.mention.mention import MentionHandler
from unittest.mock import patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.test_lib import StubBotHandler
class TestMentionBot(BotTestCase):
class TestMentionBot(BotTestCase, DefaultTests):
bot_name = "mention"
def test_bot_responds_to_empty_message(self) -> None:

View file

@ -10,7 +10,7 @@ import zulip_bots.bots.merels.merels
import zulip_bots.test_lib
class TestFollowUpBot(zulip_bots.test_lib.BotTestCase):
class TestFollowUpBot(zulip_bots.test_lib.BotTestCase, DefaultTests):
bot_name = "merels"
def test_no_command(self):

View file

@ -2,13 +2,13 @@ import unittest
from unittest import mock
from importlib import import_module
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
# TODO: Figure out a way to test bots that depends
# on the Python version of the environment.
@unittest.skip("Fails on Python3.4.")
class TestMonkeyTestitBot(BotTestCase):
class TestMonkeyTestitBot(BotTestCase, DefaultTests):
bot_name = "monkeytestit"
def setUp(self):

View file

@ -1,4 +1,4 @@
from zulip_bots.test_lib import BotTestCase, StubBotHandler, read_bot_fixture_data
from zulip_bots.test_lib import BotTestCase, DefaultTests, StubBotHandler, read_bot_fixture_data
import simple_salesforce
from simple_salesforce.exceptions import SalesforceAuthenticationFailed
from contextlib import contextmanager
@ -107,7 +107,7 @@ mock_object_types = {
}
class TestSalesforceBot(BotTestCase):
class TestSalesforceBot(BotTestCase, DefaultTests):
bot_name = "salesforce" # type: str
def _test(self, test_name: str, message: str, response: str, auth_success: bool=True) -> None:

View file

@ -1,7 +1,7 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.request_test_lib import mock_request_exception
class TestStackoverflowBot(BotTestCase):
class TestStackoverflowBot(BotTestCase, DefaultTests):
bot_name = "stack_overflow"
def test_bot(self) -> None:

View file

@ -1,10 +1,11 @@
from zulip_bots.test_lib import (
BotTestCase,
DefaultTests,
)
from typing import Any
class TestSusiBot(BotTestCase):
class TestSusiBot(BotTestCase, DefaultTests):
bot_name = "susi"
def test_help(self) -> None:

View file

@ -1,11 +1,11 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.game_handler import GameInstance
from unittest.mock import patch
from typing import List, Tuple, Any
class TestTicTacToeBot(BotTestCase):
class TestTicTacToeBot(BotTestCase, DefaultTests):
bot_name = 'tictactoe'
# FIXME: Add tests for computer moves

View file

@ -1,7 +1,7 @@
from unittest.mock import patch
from zulip_bots.bots.trello.trello import TrelloHandler
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.test_lib import StubBotHandler
mock_config = {
@ -10,7 +10,7 @@ mock_config = {
'user_name': 'TEST'
}
class TestTrelloBot(BotTestCase):
class TestTrelloBot(BotTestCase, DefaultTests):
bot_name = "trello" # type: str
def test_bot_responds_to_empty_message(self) -> None:

View file

@ -5,6 +5,7 @@ from typing import Optional
from zulip_bots.test_lib import (
BotTestCase,
DefaultTests,
read_bot_fixture_data,
)
@ -12,7 +13,7 @@ from zulip_bots.bots.trivia_quiz.trivia_quiz import (
get_quiz_from_payload,
)
class TestTriviaQuizBot(BotTestCase):
class TestTriviaQuizBot(BotTestCase, DefaultTests):
bot_name = "trivia_quiz" # type: str
new_question_response = '\nQ: Which class of animals are newts members of?\n\n' + \

View file

@ -1,6 +1,7 @@
from zulip_bots.test_lib import (
StubBotHandler,
BotTestCase,
DefaultTests,
get_bot_message_handler,
)
from zulip_bots.test_file_utils import (
@ -12,7 +13,7 @@ import os
import json
class TestTwitpostBot(BotTestCase):
class TestTwitpostBot(BotTestCase, DefaultTests):
bot_name = "twitpost"
mock_config = {'consumer_key': 'abcdefghijklmnopqrstuvwxy',
'consumer_secret': 'aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyy',

View file

@ -1,8 +1,8 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.bots.virtual_fs.virtual_fs import sample_conversation
from unittest.mock import patch
class TestVirtualFsBot(BotTestCase):
class TestVirtualFsBot(BotTestCase, DefaultTests):
bot_name = "virtual_fs"
help_txt = ('foo@example.com:\n\nThis bot implements a virtual file system for a stream.\n'
'The locations of text are persisted for the lifetime of the bot\n'

View file

@ -1,9 +1,9 @@
from unittest.mock import patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from typing import Optional
class TestWeatherBot(BotTestCase):
class TestWeatherBot(BotTestCase, DefaultTests):
bot_name = "weather"
help_content = '''

View file

@ -1,7 +1,7 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from zulip_bots.request_test_lib import mock_request_exception
class TestWikipediaBot(BotTestCase):
class TestWikipediaBot(BotTestCase, DefaultTests):
bot_name = "wikipedia"
def test_bot(self) -> None:

View file

@ -1,9 +1,9 @@
from unittest.mock import patch
import sys
from typing import Dict, Any, Optional
from zulip_bots.test_lib import BotTestCase, get_bot_message_handler, StubBotHandler
from zulip_bots.test_lib import BotTestCase, DefaultTests, get_bot_message_handler, StubBotHandler
class TestWitaiBot(BotTestCase):
class TestWitaiBot(BotTestCase, DefaultTests):
bot_name = 'witai'
MOCK_CONFIG_INFO = {

View file

@ -1,8 +1,8 @@
from unittest import mock
from unittest.mock import MagicMock, patch
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
class TestXkcdBot(BotTestCase):
class TestXkcdBot(BotTestCase, DefaultTests):
bot_name = "xkcd"
def test_latest_command(self) -> None:

View file

@ -1,9 +1,9 @@
from zulip_bots.bots.yoda.yoda import ServiceUnavailableError
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import BotTestCase, DefaultTests
from typing import Optional
class TestYodaBot(BotTestCase):
class TestYodaBot(BotTestCase, DefaultTests):
bot_name = "yoda"
help_text = '''

View file

@ -1,9 +1,9 @@
from unittest.mock import patch
from requests.exceptions import HTTPError, ConnectionError
from zulip_bots.test_lib import StubBotHandler, BotTestCase, get_bot_message_handler
from zulip_bots.test_lib import StubBotHandler, BotTestCase, DefaultTests, get_bot_message_handler
from typing import Any, Union, Dict
class TestYoutubeBot(BotTestCase):
class TestYoutubeBot(BotTestCase, DefaultTests):
bot_name = "youtube"
normal_config = {'key': '12345678',
'number_of_results': '5',

View file

@ -88,6 +88,29 @@ class StubBotHandler:
raise Exception('The bot is giving too many responses for some reason.')
class DefaultTests:
bot_name = ''
def make_request_message(self, content: str) -> Dict[str, Any]:
raise NotImplementedError()
def get_response(self, message: Dict[str, Any]) -> Dict[str, Any]:
raise NotImplementedError()
def test_bot_usage(self) -> None:
bot = get_bot_message_handler(self.bot_name)
assert bot.usage() != ''
def test_bot_responds_to_empty_message(self) -> None:
message = self.make_request_message('')
# get_response will fail if we don't respond at all
response = self.get_response(message)
# we also want a non-blank response
assert len(response['content']) >= 1
class BotTestCase(unittest.TestCase):
bot_name = ''
@ -154,19 +177,6 @@ class BotTestCase(unittest.TestCase):
bot_class = type(get_bot_message_handler(self.bot_name))
bot_class.validate_config(config_data)
def test_bot_usage(self) -> None:
bot = get_bot_message_handler(self.bot_name)
self.assertNotEqual(bot.usage(), '')
def test_bot_responds_to_empty_message(self) -> None:
message = self.make_request_message('')
# get_response will fail if we don't respond at all
response = self.get_response(message)
# we also want a non-blank response
self.assertTrue(len(response['content']) >= 1)
def mock_http_conversation(self, test_name: str) -> Any:
assert test_name is not None
http_data = read_bot_fixture_data(self.bot_name, test_name)