def ctx(): # TODO: make MockContext more usable in a "don't care about results" mode # NOTE: this is ugly but whatever. MockContext.run_command = property(lambda self: self.run.call_args[0][0]) mc = MockContext(run=Result()) mc._set(run=Mock(wraps=mc.run)) yield mc
def sudo_also_covered(self): c = MockContext(sudo=Result(stderr="super duper")) assert c.sudo("doesn't mattress").stderr == "super duper" try: MockContext().sudo("meh") except NotImplementedError: pass else: assert False, "Did not get a NotImplementedError for sudo!"
def is_undefined_if_specific_commit_checkout(self): # Just a sanity check; current logic doesn't differentiate between e.g. # 'gobbledygook' and 'HEAD'. c = MockContext(run=Result("HEAD")) assert _release_line(c)[1] == Release.UNDEFINED
def _mock_context(self): """ Context manager for a mocked Invoke context + other external patches. Specifically: - Examine test class attributes for configuration; this allows easy multidimensional test setup. - Where possible, the code under test relies on calling shell commands via the Context object, so we pass in a MockContext for that. - Where not possible (eg things which must be Python-level and not shell-level, such as version imports), mock with the 'mock' lib as usual. The MockContext's `run` method has been further mocked by wrapping it in a pass-through `mock.Mock`. It will act like regular `MockContext.run` (returning the result value it's been configured to return) but will be a `mock.Mock` object and thus exhibit all the usual call-tracking attributes and methods such as ``.called``, ``.call_args_list``, etc. :yields: an `invoke.context.MockContext` created & modified as described above. """ # # Generate config & context from attrs # changelog_file = '{0}.rst'.format(self._changelog) config = Config( overrides={ 'packaging': { 'changelog_file': path.join(support_dir, changelog_file), 'package': FAKE_PACKAGE, }, }) tag_output = "" if hasattr(self, '_tags'): tag_output = "\n".join(self._tags) + "\n" # TODO: if/when regex implemented for MockContext, make these keys less # strictly tied to the real implementation. # NOTE: Result first posarg is stdout string data. run_results = { # Branch detection "git rev-parse --abbrev-ref HEAD": Result(self._branch), # Changelog update action - just here so it can be called "$EDITOR {0.packaging.changelog_file}".format(config): Result(), # Version file update - ditto "$EDITOR {0}/_version.py".format(FAKE_PACKAGE): Result(), # Git tags "git tag": Result(tag_output), # Git status/commit/tagging # TODO: yea I'd really like regexen now plz sigh "git tag 1.1.2": Result(""), "git commit -am \"Cut 1.1.2\"": Result(""), # NOTE: some tests will need to override this, for now default to a # result that implies a commit is needed "git status --porcelain | egrep -v \"^\\?\"": Result("M somefile", exited=0), } context = MockContext(config=config, run=run_results) # Wrap run() in a Mock too. # NOTE: we don't do this inside MockContext itself because that would add a # test lib as a runtime dependency =/ # NOTE: end-running around Context/DataProxy setattr because doing # context.run.echo = True (or similar) is too common a use case to be worth # breaking just for stupid test monkeypatch purposes object.__setattr__(context, 'run', Mock(wraps=context.run)) # # Execute converge() inside a mock environment # # Allow targeted import mocking, leaving regular imports alone. real_import = __import__ def fake_import(*args, **kwargs): if args[0] is not FAKE_PACKAGE: return real_import(*args, **kwargs) return Mock(_version=Mock(__version__=self._version)) # Because I can't very well patch six.moves.builtins itself, can I? =/ builtins = '__builtin__' if PY2 else 'builtins' import_patcher = patch( '{0}.__import__'.format(builtins), side_effect=fake_import, ) with import_patcher: yield context
def assumes_feature_if_master(self): c = MockContext(run=Result("master")) assert _release_line(c)[1] == Release.FEATURE
def run(self): mc = MockContext(run=[Result('foo')]) with raises(TypeError): mc.set_result_for('run', 'whatever', Result('bar'))
def sudo(self): mc = MockContext(sudo={"foo": Result("bar")}) assert mc.sudo("foo").stdout == "bar" mc.set_result_for("sudo", "foo", Result("biz")) assert mc.sudo("foo").stdout == "biz"
def sudo(self): mc = MockContext(sudo={'foo': Result('bar')}) assert mc.sudo('foo').stdout == 'bar' mc.set_result_for('sudo', 'foo', Result('biz')) assert mc.sudo('foo').stdout == 'biz'
def return_value_map_kwargs_may_take_iterables_too(self): c = MockContext(run={"foo": [Result("bar"), Result("biz")]}) assert c.run("foo").stdout == "bar" assert c.run("foo").stdout == "biz"
def as_singleton_args(self): assert MockContext(run=True).run("anything").ok assert not MockContext(run=False).run("anything", warn=True).ok
def as_iterables(self): mc = MockContext(run=[True, False]) assert mc.run("anything").ok assert not mc.run("anything", warn=True).ok
def return_value_map_kwargs_may_take_iterables_too(self): c = MockContext(run={"foo": (Result("bar"), Result("biz"))}) assert c.run("foo").stdout == "bar" assert c.run("foo").stdout == "biz"
def regexen_return_value_map_keys_match_on_command(self): c = MockContext( run={"string": Result("yup"), re.compile(r"foo.*"): Result("bar")} ) assert c.run("string").stdout == "yup" assert c.run("foobar").stdout == "bar"
def return_value_kwargs_may_be_command_string_maps(self): c = MockContext(run={"foo": Result("bar")}) assert c.run("foo").stdout == "bar"
def return_value_kwargs_can_take_iterables_too(self): c = MockContext(run=(Result("some output"), Result("more!"))) assert c.run("doesn't mattress").stdout == "some output" assert c.run("still doesn't mattress").stdout == "more!"
def non_config_init_kwargs_used_as_return_values_for_methods(self): c = MockContext(run=Result("some output")) assert c.run("doesn't mattress").stdout == "some output"
def return_value_map_kwargs_may_take_iterables_too(self): c = MockContext(run={'foo': [Result("bar"), Result("biz")]}) eq_(c.run("foo").stdout, "bar") eq_(c.run("foo").stdout, "biz")
def as_dict_values(self): mc = MockContext(run=dict(foo=True, bar=False)) assert mc.run("foo").ok assert not mc.run("bar", warn=True).ok
def sudo(self): mc = MockContext(sudo=[Result('foo')]) mc.set_result_for('sudo', 'whatever', Result('bar'))
def as_singleton_args(self): assert MockContext(run="foo").run("anything").stdout == "foo"
def return_value_kwargs_can_take_iterables_too(self): c = MockContext(run=[Result("some output"), Result("more!")]) assert c.run("doesn't mattress").stdout == "some output" assert c.run("still doesn't mattress").stdout == "more!"
def as_iterables(self): mc = MockContext(run=["definition", "of", "insanity"]) assert mc.run("anything").stdout == "definition" assert mc.run("anything").stdout == "of" assert mc.run("anything").stdout == "insanity"
def as_dict_values(self): mc = MockContext(run=dict(foo="foo", bar="bar")) assert mc.run("foo").stdout == "foo" assert mc.run("bar").stdout == "bar"
def run(self): mc = MockContext(run={"foo": Result("bar")}) assert mc.run("foo").stdout == "bar" mc.set_result_for("run", "foo", Result("biz")) assert mc.run("foo").stdout == "biz"
def when_not_set_or_falsey(self, kwargs): c = MockContext(run={"foo": Result("bar", **kwargs)}) assert c.run("foo").command == "foo"
def wraps_when_unittest_mock_importable(self, clean_sys_modules): sys.modules["mock"] = None sys.modules["unittest.mock"] = self.mock_module mc = MockContext(**self.kwargs) assert isinstance(mc.run, Mock) assert isinstance(mc.sudo, Mock)
def does_not_occur_when_truthy(self): # Not sure why you'd want this but whatevs! c = MockContext(run={"foo": Result("bar", command="nope")}) assert c.run("foo").command == "nope" # not "bar"
def assumes_bugfix_if_release_branch(self): c = MockContext(run=Result("2.7")) assert _release_line(c)[1] == Release.BUGFIX
def methods_with_no_kwarg_values_raise_NotImplementedError(self): with raises(NotImplementedError): MockContext().run("onoz I did not anticipate this would happen")
def is_undefined_if_arbitrary_branch_name(self): c = MockContext(run=Result("yea-whatever")) assert _release_line(c)[1] == Release.UNDEFINED
def single_value(self): self._expect_NotImplementedError(MockContext(run=Result("meh")))
def return_value_kwargs_may_be_command_string_maps(self): c = MockContext(run={'foo': Result("bar")}) eq_(c.run("foo").stdout, "bar")
def iterable(self): self._expect_NotImplementedError(MockContext(run=[Result("meh")]))
def run(self): mc = MockContext(run=[Result('foo')]) mc.set_result_for('run', 'whatever', Result('bar'))
def mapping_to_single_value(self): self._expect_NotImplementedError( MockContext(run={"something": Result("meh")}) )
def run(self): mc = MockContext(run={'foo': Result('bar')}) assert mc.run('foo').stdout == 'bar' mc.set_result_for('run', 'foo', Result('biz')) assert mc.run('foo').stdout == 'biz'
def mapping_to_iterable(self): self._expect_NotImplementedError( MockContext(run={"something": [Result("meh")]}) )
def run(self): mc = MockContext(run=[Result("foo")]) with raises(TypeError): mc.set_result_for("run", "whatever", Result("bar"))
def unexpected_kwarg_type_yields_TypeError(self): with raises(TypeError): MockContext(run=123)
def sudo(self): mc = MockContext(sudo=[Result('foo')]) with raises(TypeError): mc.set_result_for('sudo', 'whatever', Result('bar'))
def sudo(self): mc = MockContext(sudo=[Result("foo")]) with raises(TypeError): mc.set_result_for("sudo", "whatever", Result("bar"))