jenkins-bot has submitted this change and it was merged.
Change subject: [FEAT] Support assertAPIError ......................................................................
[FEAT] Support assertAPIError
To assert a specific APIError the base class supports `assertAPIError` which wraps around `assertRaises` and additionally checks if the error is of a specific code and optionally a specific info. Like `assertRaises` it can be used as a context manager when additional checks should be done afterwards.
Change-Id: Ib211974ec3acd17fb45c2280e084723257a88650 --- M tests/aspects.py M tests/upload_tests.py M tests/utils.py 3 files changed, 72 insertions(+), 6 deletions(-)
Approvals: John Vandenberg: Looks good to me, approved jenkins-bot: Verified
diff --git a/tests/aspects.py b/tests/aspects.py index 0503077..2dfcc09 100644 --- a/tests/aspects.py +++ b/tests/aspects.py @@ -56,7 +56,7 @@ from tests import unittest, patch_request, unpatch_request from tests.utils import ( add_metaclass, execute_pwb, DrySite, DryRequest, - WarningSourceSkipContextManager, + WarningSourceSkipContextManager, AssertAPIErrorContextManager, )
OSWIN32 = (sys.platform == 'win32') @@ -263,6 +263,33 @@
assertPagelistTitles = assertPageTitlesEqual
+ def assertAPIError(self, code, info=None, callable_obj=None, *args, + **kwargs): + """ + Assert that a specific APIError wrapped around L{assertRaises}. + + If no callable object is defined and it returns a context manager, that + context manager will return the underlying context manager used by + L{assertRaises}. So it's possible to access the APIError by using it's + C{exception} attribute. + + @param code: The code of the error which must have happened. + @type code: str + @param info: The info string of the error or None if no it shouldn't be + checked. + @type info: str or None + @param callable_obj: The object that will be tested. If None it returns + a context manager like L{assertRaises}. + @type callable_obj: callable + @param args: The positional arguments forwarded to the callable object. + @param kwargs: The keyword arguments forwared to the callable object. + @return: The context manager if callable_obj is None and None otherwise. + @rtype: None or context manager + """ + msg = kwargs.pop('msg', None) + return AssertAPIErrorContextManager( + code, info, msg, self).handle(callable_obj, args, kwargs) +
class TestTimerMixin(TestCaseBase):
diff --git a/tests/upload_tests.py b/tests/upload_tests.py index a7987d8..7968f01 100644 --- a/tests/upload_tests.py +++ b/tests/upload_tests.py @@ -15,8 +15,6 @@
import pywikibot
-from pywikibot.data.api import APIError - from tests import join_images_path from tests.aspects import unittest, TestCase
@@ -103,9 +101,8 @@ self._finish_upload(chunk_size, self.sounds_png)
# Check if it's still cached - with self.assertRaises(APIError) as cm: + with self.assertAPIError('siiinvalidsessiondata') as cm: self.site.stash_info(self._file_key) - self.assertEqual(cm.exception.code, 'siiinvalidsessiondata') self.assertTrue(cm.exception.info.startswith('File not found'), 'info ({0}) did not start with ' '"File not found"'.format(cm.exception.info)) diff --git a/tests/utils.py b/tests/utils.py index 7cb96d6..6884cb1 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -32,7 +32,7 @@ from pywikibot import config from pywikibot.comms import threadedhttp from pywikibot.site import Namespace -from pywikibot.data.api import CachedRequest +from pywikibot.data.api import CachedRequest, APIError from pywikibot.data.api import Request as _original_Request from pywikibot.tools import ( PYTHON_VERSION, @@ -244,6 +244,48 @@ return log
+class AssertAPIErrorContextManager(object): + + """ + Context manager to assert certain APIError exceptions. + + This is build similar to the L{unittest.TestCase.assertError} implementation + which creates an context manager. It then calls L{handle} which either + returns this manager if no executing object given or calls the callable + object. + """ + + def __init__(self, code, info, msg, test_case): + """Create instance expecting the code and info.""" + self.code = code + self.info = info + self.msg = msg + self.test_case = test_case + + def __enter__(self): + """Enter this context manager and the unittest's context manager.""" + self.cm = self.test_case.assertRaises(APIError, msg=self.msg) + self.cm.__enter__() + return self.cm + + def __exit__(self, exc_type, exc_value, tb): + """Exit the context manager and assert code and optionally info.""" + result = self.cm.__exit__(exc_type, exc_value, tb) + assert result is isinstance(exc_value, APIError) + if result: + self.test_case.assertEqual(exc_value.code, self.code) + if self.info: + self.test_case.assertEqual(exc_value.info, self.info) + return result + + def handle(self, callable_obj, args, kwargs): + """Handle the callable object by returning itself or using itself.""" + if callable_obj is None: + return self + with self: + callable_obj(*args, **kwargs) + + class DryParamInfo(dict):
"""Dummy class to use instead of L{pywikibot.data.api.ParamInfo}."""
pywikibot-commits@lists.wikimedia.org