jenkins-bot has submitted this change and it was merged.
Change subject: [FEAT] tests: Generic reading bot class test case ......................................................................
[FEAT] tests: Generic reading bot class test case
For tests which use a Bot class and also userPut (or similar) this test case can be used as it monkey patches '_save_page' to prevent actually saving the page.
Change-Id: Iaec972f813f2470d1f2e51fde8d59b29d929925c --- M tests/bot_tests.py M tests/disambredir_tests.py 2 files changed, 90 insertions(+), 24 deletions(-)
Approvals: John Vandenberg: Looks good to me, approved jenkins-bot: Verified
diff --git a/tests/bot_tests.py b/tests/bot_tests.py index b2a9a7c..4764e79 100644 --- a/tests/bot_tests.py +++ b/tests/bot_tests.py @@ -14,7 +14,81 @@ import pywikibot import pywikibot.bot
-from tests.aspects import unittest, DefaultSiteTestCase, SiteAttributeTestCase +from tests.aspects import ( + unittest, DefaultSiteTestCase, SiteAttributeTestCase, TestCase, +) + + +class FakeSaveBotTestCase(TestCase): + + """ + An abstract test case which patches the bot class to not actually write. + + It redirects the bot's _save_page to it's own C{bot_save} method. Currently + userPut, put_current and user_edit_entity call it. By default it'll call + the original method but replace the function called to actually save the + page by C{page_save}. It patches the bot class as soon as this class' + attribute bot is defined. It also sets the bot's 'always' option to True to + avoid user interaction. + + The C{bot_save} method compares the save counter before the call and asserts + that it has increased by one after the call. It also stores locally in + C{save_called} if C{page_save} has been called. If C{bot_save} or + C{page_save} are implemented they should call super's method at some point + to make sure these assertions work. At C{tearDown} it checks that the pages + are saved often enough. The attribute C{default_assert_saves} defines the + number of saves which must happen and compares it to the difference using + the save counter. It is possible to define C{assert_saves} after C{setUp} to + overwrite the default value for certain tests. By default the number of + saves it asserts are 1. Additionally C{save_called} increases by 1 on each + call of C{page_save} and should be equal to C{assert_saves}. + + This means if the bot class actually does other writes, like using + L{pywikibot.page.Page.save} manually, it'll still write. + """ + + @property + def bot(self): + """Get the current bot.""" + return self._bot + + @bot.setter + def bot(self, value): + """Set and patch the current bot.""" + assert value._save_page != self.bot_save, 'bot may not be patched.' + self._bot = value + self._bot.options['always'] = True + self._original = self._bot._save_page + self._bot._save_page = self.bot_save + self._old_counter = self._bot._save_counter + + def setUp(self): + """Set up test by reseting the counters.""" + super(FakeSaveBotTestCase, self).setUp() + self.assert_saves = getattr(self, 'default_assert_saves', 1) + self.save_called = 0 + + def tearDown(self): + """Tear down by asserting the counters.""" + self.assertEqual(self._bot._save_counter, + self._old_counter + self.assert_saves) + self.assertEqual(self.save_called, self.assert_saves) + super(FakeSaveBotTestCase, self).tearDown() + + def bot_save(self, page, func, *args, **kwargs): + """Handle when bot's userPut was called.""" + self.assertGreaterEqual(self._bot._save_counter, 0) + old_counter = self._bot._save_counter + old_local_cnt = self.save_called + result = self._original(page, self.page_save, *args, **kwargs) + self.assertEqual(self._bot._save_counter, old_counter + 1) + self.assertEqual(self.save_called, old_local_cnt + 1) + self.assertGreater(self._bot._save_counter, self._old_counter) + return result + + def page_save(self, *args, **kwargs): + """Handle when bot calls the page's save method.""" + self.save_called += 1
class TestBotTreatExit(object): diff --git a/tests/disambredir_tests.py b/tests/disambredir_tests.py index 5b34e47..5e260c7 100644 --- a/tests/disambredir_tests.py +++ b/tests/disambredir_tests.py @@ -18,11 +18,12 @@
from scripts import disambredir
-from tests.aspects import unittest, TestCase +from tests.aspects import unittest +from tests.bot_tests import FakeSaveBotTestCase from tests.utils import fixed_generator
-class TestDisambigurationRedirectBot(TestCase): +class TestDisambigurationRedirectBot(FakeSaveBotTestCase):
""" Test cases for DisambigurationRedirectBot. @@ -62,70 +63,61 @@ def setUpClass(cls): """Initialize page variable.""" super(TestDisambigurationRedirectBot, cls).setUpClass() + # Patch the page to be independent of the actual site cls.page = pywikibot.Page(cls.site, 'User:BobBot/Test disambig') + cls.page.linkedPages = fixed_generator( + [pywikibot.Page(cls.site, 'User:BobBot/Redir'), + pywikibot.Page(cls.site, 'Main Page')]) + + def bot_save(self, page, *args, **kwargs): + """Check if the page matches.""" + self.assertIs(page, self.page) + return super(TestDisambigurationRedirectBot, self).bot_save( + page, *args, **kwargs)
def setUp(self): """Set up the test page.""" - def _save_page(*args, **kwargs): - self.assertIs(args[0], self.page) - self.save_called = True - return # avert actually saving - super(TestDisambigurationRedirectBot, self).setUp() self.page.text = ('[[User:BobBot/Redir#Foo|Bar]]\n' '[[User:BobBot/Redir|Baz]]\n' '[[Main Page|Label]]\n') self.bot = disambredir.DisambiguationRedirectBot(generator=[self.page]) - self.bot.options['always'] = True - # Patch the page and bot to not actually save anything - self.page.linkedPages = fixed_generator( - [pywikibot.Page(self.site, 'User:BobBot/Redir'), - pywikibot.Page(self.site, 'Main Page')]) - self.save_called = False - self.bot._save_page = _save_page
def test_unchanged(self): """Test no change.""" + # No changes needed, won't call the save method + self.assert_saves = 0 self._patch_create_callback('n') - self.assertFalse(self.save_called) self.bot.run() self.assertEqual(self.page.text, '[[User:BobBot/Redir#Foo|Bar]]\n' '[[User:BobBot/Redir|Baz]]\n' '[[Main Page|Label]]\n') - # No changes needed, won't call the save method - self.assertFalse(self.save_called)
def test_unlink(self): """Test unlinking.""" self._patch_create_callback('u') - self.assertFalse(self.save_called) self.bot.run() self.assertEqual(self.page.text, 'Bar\nBaz\n[[Main Page|Label]]\n') - self.assertTrue(self.save_called)
def test_replace_target(self): """Test replacing just target page.""" self._patch_create_callback('t') - self.assertFalse(self.save_called) self.bot.run() self.assertEqual(self.page.text, '[[Main Page#Foo|Bar]]\n' '[[Main Page|Baz]]\n' '[[Main Page|Label]]\n') - self.assertTrue(self.save_called)
def test_replace_all(self): """Test replacing target and label.""" self._patch_create_callback('l') - self.assertFalse(self.save_called) self.bot.run() self.assertEqual(self.page.text, '[[Main Page#Foo|Main Page]]\n' '[[Main Page]]\n' '[[Main Page|Label]]\n') - self.assertTrue(self.save_called)
if __name__ == '__main__':