diff --git a/ops/testing.py b/ops/testing.py index b42c111c8..f4ab5fe3a 100755 --- a/ops/testing.py +++ b/ops/testing.py @@ -15,10 +15,11 @@ import inspect import pathlib import random -from textwrap import dedent import tempfile import typing import yaml +from contextlib import contextmanager +from textwrap import dedent from ops import ( charm, @@ -321,6 +322,24 @@ def enable_hooks(self) -> None: """ self._hooks_enabled = True + @contextmanager + def hooks_disabled(self): + """A context manager to run code with hooks disabled. + + Example:: + + with harness.hooks_disabled(): + # things in here don't fire events + harness.set_leader(True) + harness.update_config(unset=['foo', 'bar']) + # things here will again fire events + """ + self.disable_hooks() + try: + yield None + finally: + self.enable_hooks() + def _next_relation_id(self): rel_id = self._relation_id_counter self._relation_id_counter += 1 diff --git a/test/test_testing.py b/test/test_testing.py index d054c426a..d1c61d8df 100644 --- a/test/test_testing.py +++ b/test/test_testing.py @@ -540,6 +540,28 @@ def test_hooks_enabled_and_disabled(self): harness.charm.get_changes(reset=True), [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}]) + def test_hooks_disabled_contextmanager(self): + harness = Harness(RecordingCharm, meta=''' + name: test-charm + ''') + self.addCleanup(harness.cleanup) + # Before begin() there are no events. + harness.update_config({'value': 'first'}) + # By default, after begin the charm is set up to receive events. + harness.begin() + harness.update_config({'value': 'second'}) + self.assertEqual( + harness.charm.get_changes(reset=True), + [{'name': 'config-changed', 'data': {'value': 'second'}}]) + # Once disabled, we won't see config-changed when we make an update + with harness.hooks_disabled(): + harness.update_config({'third': '3'}) + self.assertEqual(harness.charm.get_changes(reset=True), []) + harness.update_config({'value': 'fourth'}) + self.assertEqual( + harness.charm.get_changes(reset=True), + [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}]) + def test_metadata_from_directory(self): tmp = pathlib.Path(tempfile.mkdtemp()) self.addCleanup(shutil.rmtree, str(tmp))