def test_add_cleanup_with_known_deeper_layer2(self): my_cleanup = Mock(spec=cleanup_func) context = Context(runner=Mock()) with scoped_context_layer(context, layer="feature"): with scoped_context_layer(context, layer="scenario"): context.add_cleanup(my_cleanup, layer="feature") my_cleanup.assert_not_called() # CALLS-HERE: context._pop() my_cleanup.assert_called_once()
def test_cleanup_funcs_on_two_context_frames(self): call_listener = CallListener() my_cleanup_A1 = CleanupFunction("CLEANUP_A1", listener=call_listener) my_cleanup_A2 = CleanupFunction("CLEANUP_A2", listener=call_listener) my_cleanup_B1 = CleanupFunction("CLEANUP_B1", listener=call_listener) my_cleanup_B2 = CleanupFunction("CLEANUP_B2", listener=call_listener) my_cleanup_B3 = CleanupFunction("CLEANUP_B3", listener=call_listener) my_cleanup_A1M = Mock(side_effect=my_cleanup_A1) my_cleanup_A2M = Mock(side_effect=my_cleanup_A2) my_cleanup_B1M = Mock(side_effect=my_cleanup_B1) my_cleanup_B2M = Mock(side_effect=my_cleanup_B2) my_cleanup_B3M = Mock(side_effect=my_cleanup_B3) # -- SETUP: context = Context(runner=Mock()) with scoped_context_layer(context): # -- LAYER A: context.add_cleanup(my_cleanup_A1M) context.add_cleanup(my_cleanup_A2M) with scoped_context_layer(context): # -- LAYER B: context.add_cleanup(my_cleanup_B1M) context.add_cleanup(my_cleanup_B2M) context.add_cleanup(my_cleanup_B3M) my_cleanup_B1M.assert_not_called() my_cleanup_B2M.assert_not_called() my_cleanup_B3M.assert_not_called() # -- context.pop(LAYER_B): Call cleanups for Bx expected_call_order = [ "called:CLEANUP_B3", "called:CLEANUP_B2", "called:CLEANUP_B1", ] assert call_listener.collected == expected_call_order my_cleanup_A1M.assert_not_called() my_cleanup_A2M.assert_not_called() my_cleanup_B1M.assert_called_once() my_cleanup_B2M.assert_called_once() my_cleanup_B3M.assert_called_once() # -- context.pop(LAYER_A): Call cleanups for Ax expected_call_order = [ "called:CLEANUP_B3", "called:CLEANUP_B2", "called:CLEANUP_B1", "called:CLEANUP_A2", "called:CLEANUP_A1", ] assert call_listener.collected == expected_call_order my_cleanup_A1M.assert_called_once() my_cleanup_A2M.assert_called_once() my_cleanup_B1M.assert_called_once() my_cleanup_B2M.assert_called_once() my_cleanup_B3M.assert_called_once()
def test_can_use_fixture_two_times(self): """Ensures that a fixture can be used multiple times (with different names) within a context layer. """ @fixture def foo(context, checkpoints, *args, **kwargs): fixture_object = FooFixture.setup(*args, **kwargs) setattr(context, fixture_object.name, fixture_object) checkpoints.append("foo.setup:%s" % fixture_object.name) yield fixture_object checkpoints.append("foo.cleanup:%s" % fixture_object.name) fixture_object.cleanup() checkpoints = [] context = make_runtime_context() with scoped_context_layer(context): the_fixture1 = use_fixture(foo, context, checkpoints, name="foo_1") the_fixture2 = use_fixture(foo, context, checkpoints, name="foo_2") # -- VERIFY: Fixture and context setup was performed. assert checkpoints == ["foo.setup:foo_1", "foo.setup:foo_2"] assert context.foo_1 is the_fixture1 assert context.foo_2 is the_fixture2 assert the_fixture1 is not the_fixture2 checkpoints.append("scoped-block") # -- VERIFY: Fixture and context cleanup is performed. assert_context_cleanup(context, "foo_1") assert_context_cleanup(context, "foo_2") assert checkpoints == [ "foo.setup:foo_1", "foo.setup:foo_2", "scoped-block", "foo.cleanup:foo_2", "foo.cleanup:foo_1" ]
def test_data_schema2(self): @fixture def foo(context, *args, **kwargs): # -- NOTE checkpoints: Injected from outer scope. params = "%r, %r" % (args, kwargs) checkpoints.append("foo.setup: %s" % params) yield "fixture.foo" checkpoints.append("foo.cleanup: %s" % params) fixture_registry = { "fixture.foo": fixture_call_params(foo, 1, 2, 3, name="foo_1") } # -- PERFORM-TEST: context = make_runtime_context() checkpoints = [] with scoped_context_layer(context): use_fixture_by_tag("fixture.foo", context, fixture_registry) checkpoints.append("scoped-block") # -- VERIFY: assert checkpoints == [ "foo.setup: (1, 2, 3), {'name': 'foo_1'}", "scoped-block", "foo.cleanup: (1, 2, 3), {'name': 'foo_1'}", ]
def test_simplistic_composite_with_setup_error_skips_cleanup(self): def setup_bad_fixture_with_error(text): raise FixtureSetupError("OOPS") @fixture def sad_composite2(context, checkpoints, *args, **kwargs): checkpoints.append("foo.setup:_1") the_fixture1 = FooFixture.setup(*args, **kwargs) checkpoints.append("bad.setup_with_error") the_fixture2 = setup_bad_fixture_with_error(text="OOPS") checkpoints.append("bad.setup.done:NOT_REACHED") yield (the_fixture1, the_fixture2) checkpoints.append("foo.cleanup:_1:NOT_REACHED") # -- PERFORM-TEST: context = make_runtime_context() checkpoints = [] with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(sad_composite2, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") # -- VERIFY: # * SAD: fixture1-cleanup is not called after fixture2-setup-error assert checkpoints == ["foo.setup:_1", "bad.setup_with_error"]
def test_invalid_data_schema_raises_value_error(self): @fixture def foo(context, *args, **kwargs): pass class BadFixtureData(object): def __init__(self, fixture_func, *args, **kwargs): self.fixture_func = fixture_func self.fixture_args = args self.fixture_kwargs = kwargs fixture_registry = { "fixture.foo": BadFixtureData(foo, 1, 2, 3, name="foo_1") } # -- PERFORM-TEST: context = make_runtime_context() with pytest.raises(ValueError) as exc_info: with scoped_context_layer(context): use_fixture_by_tag("fixture.foo", context, fixture_registry) # -- VERIFY: expected = "fixture_data: Expected tuple or fixture-func, but is:" assert expected in str(exc_info.value) assert "BadFixtureData object" in str(exc_info.value)
def test_bad_with_setup_and_cleanup_error(self): # -- GOOD: cleanup_fixture() part is called when setup-error occurs. # BUT: FixtureSetupError is hidden by FixtureCleanupError @fixture def bad_with_setup_and_cleanup_error(context, checkpoints, *args, **kwargs): def cleanup_bad_with_error(): checkpoints.append("bad.cleanup_with_error") raise FixtureCleanupError() checkpoints.append("bad.setup_with_error") context.add_cleanup(cleanup_bad_with_error) raise FixtureSetupError() return FooFixture("NOT_REACHED") # -- PERFORM TEST: checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_setup_and_cleanup_error with pytest.raises(FixtureCleanupError) as exc_info: with scoped_context_layer(context): use_fixture(bad_fixture, context, checkpoints, name="BAD2") checkpoints.append("scoped-block:NOT_REACHED") # -- VERIFY: Ensure normal cleanup-parts were performed or tried. # OOPS: FixtureCleanupError in fixture-cleanup hides FixtureSetupError assert checkpoints == ["bad.setup_with_error", "bad.cleanup_with_error"] assert isinstance(exc_info.value, FixtureCleanupError), "LAST-EXCEPTION-WINS"
def test_use_composite_fixture_with_block_error(self): @fixture def fixture_foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "foo") checkpoints.append("foo.setup:%s" % fixture_name) yield checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def composite2(context, checkpoints, *args, **kwargs): the_composite = use_composite_fixture_with(context, [ fixture_call_params(fixture_foo, checkpoints, name="_1"), fixture_call_params(fixture_foo, checkpoints, name="_2"), ]) return the_composite # -- PERFORM-TEST: checkpoints = [] context = make_runtime_context() with pytest.raises(RuntimeError): with scoped_context_layer(context): use_fixture(composite2, context, checkpoints) checkpoints.append("scoped-block_with_error") raise RuntimeError("OOPS") checkpoints.append("scoped-block.done:NOT_REACHED") # -- ENSURES: # * fixture1-cleanup/cleanup is called even scoped-block-error # * fixture2-cleanup/cleanup is called even scoped-block-error # * fixture-cleanup occurs in reversed setup-order assert checkpoints == [ "foo.setup:_1", "foo.setup:_2", "scoped-block_with_error", "foo.cleanup:_2", "foo.cleanup:_1" ]
def test_bad_with_cleanup_error_performs_all_cleanups(self): @fixture def foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "") checkpoints.append("foo.setup:%s" % fixture_name) yield FooFixture(*args, **kwargs) checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def bad_with_cleanup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup") yield FooFixture(*args, **kwargs) checkpoints.append("bad.cleanup_with_error") raise FixtureCleanupError() checkpoints.append("bad.cleanup.done:NOT_REACHED") # -- PERFORM TEST: the_fixture1 = None the_fixture2 = None the_fixture3 = None checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_cleanup_error with pytest.raises(FixtureCleanupError): with scoped_context_layer(context): the_fixture1 = use_fixture(foo, context, checkpoints, name="foo_1") the_fixture2 = use_fixture(bad_fixture, context, checkpoints, name="BAD") the_fixture3 = use_fixture(foo, context, checkpoints, name="foo_3") checkpoints.append("scoped-block") # -- VERIFY: Tries to perform all cleanups even when cleanup-error(s) occur. assert checkpoints == [ "foo.setup:foo_1", "bad.setup", "foo.setup:foo_3", "scoped-block", "foo.cleanup:foo_3", "bad.cleanup_with_error", "foo.cleanup:foo_1"]
def test_simplistic_composite_with_block_error_performs_cleanup(self): def setup_bad_fixture_with_error(text): raise FixtureSetupError("OOPS") @fixture def simplistic_composite2(context, checkpoints, *args, **kwargs): checkpoints.append("foo.setup:_1") the_fixture1 = FooFixture.setup(*args, name="_1") checkpoints.append("foo.setup:_2") the_fixture2 = FooFixture.setup(*args, name="_2") yield (the_fixture1, the_fixture2) checkpoints.append("foo.cleanup:_1") the_fixture1.cleanup() checkpoints.append("foo.cleanup:_2") the_fixture2.cleanup() # -- PERFORM-TEST: context = make_runtime_context() checkpoints = [] with pytest.raises(RuntimeError): with scoped_context_layer(context): use_fixture(simplistic_composite2, context, checkpoints) checkpoints.append("scoped-block_with_error") raise RuntimeError("OOPS") checkpoints.append("scoped-block.end:NOT_REACHED") # -- VERIFY: # * fixture1-setup/cleanup is called when block-error occurs # * fixture2-setup/cleanup is called when block-error occurs # * fixture-cleanups occurs in specified composite-cleanup order assert checkpoints == [ "foo.setup:_1", "foo.setup:_2", "scoped-block_with_error", "foo.cleanup:_1", "foo.cleanup:_2" ]
def test_on_cleanup_error__may_be_called_several_times_per_cleanup(self): def bad_cleanup1(): raise RuntimeError("CLEANUP_1") def bad_cleanup2(): raise RuntimeError("CLEANUP_2") class CleanupErrorCollector(object): def __init__(self): self.collected = [] def __call__(self, context, cleanup_func, exception): self.collected.append((context, cleanup_func, exception)) context = Context(runner=Mock()) collect_cleanup_error = CleanupErrorCollector() with pytest.raises(RuntimeError): with scoped_context_layer(context): context.on_cleanup_error = collect_cleanup_error context.add_cleanup(bad_cleanup1) context.add_cleanup(bad_cleanup2) expected = [ (context, bad_cleanup2, RuntimeError("CLEANUP_2")), (context, bad_cleanup1, RuntimeError("CLEANUP_1")), ] assert len(collect_cleanup_error.collected) == 2 assert collect_cleanup_error.collected[0][:-1] == expected[0][:-1] assert collect_cleanup_error.collected[1][:-1] == expected[1][:-1]
def test_setup_error_with_context_cleanup2_then_cleanup_is_called(self): # -- CASE: Fixture is generator-function (contextmanager) # NOTE: Explicit use of context.add_cleanup() @fixture def foo(context, checkpoints, **kwargs): def cleanup_foo(arg=""): checkpoints.append("cleanup_foo:%s" % arg) checkpoints.append("foo.setup_with_error:foo_1") context.add_cleanup(cleanup_foo, "foo_1") raise FixtureSetupError("foo_1") checkpoints.append("foo.setup.done:NOT_REACHED") yield checkpoints.append("foo.cleanup:NOT_REACHED") checkpoints = [] context = make_runtime_context() with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(foo, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") # -- ENSURE: cleanup_foo() is called assert checkpoints == [ "foo.setup_with_error:foo_1", "cleanup_foo:foo_1" ]
def test_use_composite_fixture(self): @fixture def fixture_foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "foo") checkpoints.append("foo.setup:%s" % fixture_name) yield checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def composite2(context, checkpoints, *args, **kwargs): the_composite = use_composite_fixture_with(context, [ fixture_call_params(fixture_foo, checkpoints, name="_1"), fixture_call_params(fixture_foo, checkpoints, name="_2"), ]) return the_composite # -- PERFORM-TEST: context = make_runtime_context() checkpoints = [] with scoped_context_layer(context): use_fixture(composite2, context, checkpoints) checkpoints.append("scoped-block") assert checkpoints == [ "foo.setup:_1", "foo.setup:_2", "scoped-block", "foo.cleanup:_2", "foo.cleanup:_1", ]
def test_can_use_fixture_two_times(self): """Ensures that a fixture can be used multiple times (with different names) within a context layer. """ @fixture def foo(context, checkpoints, *args, **kwargs): fixture_object = FooFixture.setup(*args, **kwargs) setattr(context, fixture_object.name, fixture_object) checkpoints.append("foo.setup:%s" % fixture_object.name) yield fixture_object checkpoints.append("foo.cleanup:%s" % fixture_object.name) fixture_object.cleanup() checkpoints = [] context = make_runtime_context() with scoped_context_layer(context): the_fixture1 = use_fixture(foo, context, checkpoints, name="foo_1") the_fixture2 = use_fixture(foo, context, checkpoints, name="foo_2") # -- VERIFY: Fixture and context setup was performed. assert checkpoints == ["foo.setup:foo_1", "foo.setup:foo_2"] assert context.foo_1 is the_fixture1 assert context.foo_2 is the_fixture2 assert the_fixture1 is not the_fixture2 checkpoints.append("scoped-block") # -- VERIFY: Fixture and context cleanup is performed. assert_context_cleanup(context, "foo_1") assert_context_cleanup(context, "foo_2") assert checkpoints == ["foo.setup:foo_1", "foo.setup:foo_2", "scoped-block", "foo.cleanup:foo_2", "foo.cleanup:foo_1"]
def test_bad_with_setup_and_cleanup_error(self): # -- GOOD: cleanup_fixture() part is called when setup-error occurs. # BUT: FixtureSetupError is hidden by FixtureCleanupError @fixture def bad_with_setup_and_cleanup_error(context, checkpoints, *args, **kwargs): def cleanup_bad_with_error(): checkpoints.append("bad.cleanup_with_error") raise FixtureCleanupError() checkpoints.append("bad.setup_with_error") context.add_cleanup(cleanup_bad_with_error) raise FixtureSetupError() return FooFixture("NOT_REACHED") # -- PERFORM TEST: checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_setup_and_cleanup_error with pytest.raises(FixtureCleanupError) as exc_info: with scoped_context_layer(context): use_fixture(bad_fixture, context, checkpoints, name="BAD2") checkpoints.append("scoped-block:NOT_REACHED") # -- VERIFY: Ensure normal cleanup-parts were performed or tried. # OOPS: FixtureCleanupError in fixture-cleanup hides FixtureSetupError assert checkpoints == [ "bad.setup_with_error", "bad.cleanup_with_error" ] assert isinstance(exc_info.value, FixtureCleanupError), "LAST-EXCEPTION-WINS"
def test_bad_with_setup_error_aborts_on_first_error(self): @fixture def foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "") checkpoints.append("foo.setup:%s" % fixture_name) yield FooFixture(*args, **kwargs) checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def bad_with_setup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup_with_error") raise FixtureSetupError() yield FooFixture(*args, **kwargs) checkpoints.append("bad.cleanup:NOT_REACHED") # -- PERFORM-TEST: the_fixture1 = None the_fixture2 = None the_fixture3 = None checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_setup_error with pytest.raises(FixtureSetupError): with scoped_context_layer(context): the_fixture1 = use_fixture(foo, context, checkpoints, name="foo_1") the_fixture2 = use_fixture(bad_fixture, context, checkpoints, name="BAD") the_fixture3 = use_fixture(foo, context, checkpoints, name="NOT_REACHED") checkpoints.append("scoped-block:NOT_REACHED") # -- VERIFY: Ensure cleanup-parts were performed until failure-point. assert isinstance(the_fixture1, FooFixture) assert the_fixture2 is None # -- NEVER-STORED: Due to setup error. assert the_fixture3 is None # -- NEVER-CREATED: Due to bad_fixture. assert checkpoints == [ "foo.setup:foo_1", "bad.setup_with_error", "foo.cleanup:foo_1"]
def test_issue__getattr_with_protected_unknown_context_attribute_raises_no_error( ): context = Context(runner=Mock()) with scoped_context_layer(context): # CALLS-HERE: context._push() value = getattr(context, "_UNKNOWN_ATTRIB", "__UNKNOWN__") assert value == "__UNKNOWN__"
def test_add_cleanup_with_known_layer_and_kwargs(self): my_cleanup = Mock(spec=cleanup_func_with_args) context = Context(runner=Mock()) with scoped_context_layer(context, layer="scenario"): context.add_cleanup(my_cleanup, layer="scenario", name="alice") my_cleanup.assert_not_called() # CALLS-HERE: context._pop() my_cleanup.assert_called_once_with(name="alice")
def test_add_cleanup_with_args(self): my_cleanup = Mock(spec=cleanup_func_with_args) context = Context(runner=Mock()) with scoped_context_layer(context): context.add_cleanup(my_cleanup, 1, 2, 3) my_cleanup.assert_not_called() # CALLS-HERE: context._pop() my_cleanup.assert_called_once_with(1, 2, 3)
def test_add_cleanup_with_unknown_layer_raises_lookup_error(self): """Cleanup function is not registered""" my_cleanup = Mock(spec=cleanup_func) context = Context(runner=Mock()) with scoped_context_layer(context): # CALLS-HERE: context._push() with pytest.raises(LookupError) as error: context.add_cleanup(my_cleanup, layer="other") my_cleanup.assert_not_called()
def test_cleanup_func_is_called_when_context_frame_is_popped(self): my_cleanup = Mock(spec=cleanup_func) context = Context(runner=Mock()) with scoped_context_layer(context): # CALLS-HERE: context._push() context.add_cleanup(my_cleanup) # -- ENSURE: Not called before context._pop() my_cleanup.assert_not_called() # CALLS-HERE: context._pop() my_cleanup.assert_called_once()
def test_add_cleanup__rejects_noncallable_cleanup_func(self): class NonCallable(object): pass non_callable = NonCallable() context = Context(runner=Mock()) with pytest.raises(AssertionError) as e: with scoped_context_layer(context): context.add_cleanup(non_callable) assert "REQUIRES: callable(cleanup_func)" in str(e.value)
def test_unknown_fixture_raises_lookup_error(self): fixture_registry = {} # -- PERFORM-TEST: context = make_runtime_context() with pytest.raises(LookupError) as exc_info: with scoped_context_layer(context): use_fixture_by_tag("UNKNOWN_FIXTURE", context, fixture_registry) # -- VERIFY: assert "Unknown fixture-tag: UNKNOWN_FIXTURE" in str(exc_info.value)
def cleanup_steps(): logger.info(f'Teardown running for {name}') for scenario in setup_scenarios['teardown']: if any( tag.startswith('fixture.browser') for tag in scenario.tags): with scoped_context_layer(context, layer_name='setup_teardown'): run_scenario(context, scenario) else: run_scenario(context, scenario) for scenario in setup_scenarios['setup']: scenario.reset()
def test_fixture_with_kwargs(self): """Ensures that keyword args are passed to fixture function.""" @fixture def bar(context, *args, **kwargs): fixture_object = BarFixture.setup(*args, **kwargs) context.bar = fixture_object return fixture_object context = make_runtime_context() with scoped_context_layer(context): the_fixture = use_fixture(bar, context, name="bar", timeout=10) expected_kwargs = dict(name="bar", timeout=10) assert the_fixture.kwargs == expected_kwargs
def test_fixture_with_args(self): """Ensures that positional args are passed to fixture function.""" @fixture def foo(context, *args, **kwargs): fixture_object = FooFixture.setup(*args, **kwargs) context.foo = fixture_object yield fixture_object fixture_object.cleanup() context = make_runtime_context() with scoped_context_layer(context): the_fixture = use_fixture(foo, context, 1, 2, 3) expected_args = (1, 2, 3) assert the_fixture.args == expected_args
def test_on_cleanup_error__is_called_if_defined(self): def bad_cleanup(): raise RuntimeError("in CLEANUP call") def handle_cleanup_error(context, cleanup_func, exception): print("CALLED: handle_cleanup_error") context = Context(runner=Mock()) handle_cleanup_error_func = Mock(spec=handle_cleanup_error) with pytest.raises(RuntimeError): with scoped_context_layer(context): context.on_cleanup_error = handle_cleanup_error_func context.add_cleanup(bad_cleanup) handle_cleanup_error_func.assert_called_once()
def test_cleanup_funcs_are_called_when_context_frame_is_popped(self): my_cleanup1 = Mock(spec=cleanup_func) my_cleanup2 = Mock(spec=cleanup_func) # -- SETUP: context = Context(runner=Mock()) with scoped_context_layer(context): context.add_cleanup(my_cleanup1) context.add_cleanup(my_cleanup2) # -- ENSURE: Not called before context._pop() my_cleanup1.assert_not_called() my_cleanup2.assert_not_called() # -- CALLS-HERE: context._pop() my_cleanup1.assert_called_once() my_cleanup2.assert_called_once()
def setup_teardown(context, name, set_teardown=False, teardown_only=False): """ Based on a tag request (e.g. - @fixture.setup.{name}), run the steps of the @configure.{name} setup feature if the setup feature has not been previously run. Adds a cleanup step after the feature/scenario if set_teardown is True """ setup_scenarios = context.context_setup.get(name, None) if not setup_scenarios: logger.error( f'Setup Teardown was requested, but no context feature named {name} was found' ) return logger.info(f'Setup running for {name}') if not teardown_only: for scenario in setup_scenarios['setup']: if scenario.should_run(): if any( tag.startswith('fixture.browser') for tag in scenario.tags): with scoped_context_layer(context, layer_name='setup_teardown'): run_scenario(context, scenario) else: run_scenario(context, scenario) scenario.skip() if set_teardown: def cleanup_steps(): logger.info(f'Teardown running for {name}') for scenario in setup_scenarios['teardown']: if any( tag.startswith('fixture.browser') for tag in scenario.tags): with scoped_context_layer(context, layer_name='setup_teardown'): run_scenario(context, scenario) else: run_scenario(context, scenario) for scenario in setup_scenarios['setup']: scenario.reset() context.add_cleanup(cleanup_steps) return
def test_on_cleanup_error__prints_error_by_default(self, capsys): def bad_cleanup_func(): raise RuntimeError("in CLEANUP call") bad_cleanup = Mock(side_effect=bad_cleanup_func) context = Context(runner=Mock()) with pytest.raises(RuntimeError): with scoped_context_layer(context): context.add_cleanup(bad_cleanup) captured_output, _ = capsys.readouterr() bad_cleanup.assert_called() assert "CLEANUP-ERROR in " in captured_output assert "RuntimeError: in CLEANUP call" in captured_output # -- FOR DIAGNOSTICS: print(captured_output)
def test_use_fixture_with_setup_error(self): @fixture def fixture_foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "foo") checkpoints.append("foo.setup:%s" % fixture_name) yield checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def bad_with_setup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup_with_error") raise FixtureSetupError("OOPS") yield checkpoints.append("bad.cleanup:NOT_REACHED") @fixture def composite3(context, checkpoints, *args, **kwargs): bad_fixture = bad_with_setup_error the_fixture1 = use_fixture(fixture_foo, context, checkpoints, name="_1") the_fixture2 = use_fixture(bad_fixture, context, checkpoints, name="OOPS") the_fixture3 = use_fixture(fixture_foo, context, checkpoints, name="_3:NOT_REACHED") return (the_fixture1, the_fixture2, the_fixture3) # NOT_REACHED # -- PERFORM-TEST: context = make_runtime_context() checkpoints = [] with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(composite3, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") # -- ENSURES: # * fixture1-cleanup is called even after fixture2-setup-error # * fixture3-setup/cleanup are not called due to fixture2-setup-error assert checkpoints == [ "foo.setup:_1", "bad.setup_with_error", "foo.cleanup:_1" ]
def test_bad_with_setup_error_aborts_on_first_error(self): @fixture def foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "") checkpoints.append("foo.setup:%s" % fixture_name) yield FooFixture(*args, **kwargs) checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def bad_with_setup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup_with_error") raise FixtureSetupError() yield FooFixture(*args, **kwargs) checkpoints.append("bad.cleanup:NOT_REACHED") # -- PERFORM-TEST: the_fixture1 = None the_fixture2 = None the_fixture3 = None checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_setup_error with pytest.raises(FixtureSetupError): with scoped_context_layer(context): the_fixture1 = use_fixture(foo, context, checkpoints, name="foo_1") the_fixture2 = use_fixture(bad_fixture, context, checkpoints, name="BAD") the_fixture3 = use_fixture(foo, context, checkpoints, name="NOT_REACHED") checkpoints.append("scoped-block:NOT_REACHED") # -- VERIFY: Ensure cleanup-parts were performed until failure-point. assert isinstance(the_fixture1, FooFixture) assert the_fixture2 is None # -- NEVER-STORED: Due to setup error. assert the_fixture3 is None # -- NEVER-CREATED: Due to bad_fixture. assert checkpoints == [ "foo.setup:foo_1", "bad.setup_with_error", "foo.cleanup:foo_1" ]
def test_setup_eror_with_plaingen_then_cleanup_is_not_called(self): # -- CASE: Fixture is generator-function @fixture def foo(context, checkpoints): checkpoints.append("foo.setup.begin") raise FixtureSetupError("foo") checkpoints.append("foo.setup.done:NOT_REACHED") yield checkpoints.append("foo.cleanup:NOT_REACHED") checkpoints = [] context = make_runtime_context() with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(foo, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") assert checkpoints == ["foo.setup.begin"]
def test_bad_with_cleanup_error_performs_all_cleanups(self): @fixture def foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "") checkpoints.append("foo.setup:%s" % fixture_name) yield FooFixture(*args, **kwargs) checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def bad_with_cleanup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup") yield FooFixture(*args, **kwargs) checkpoints.append("bad.cleanup_with_error") raise FixtureCleanupError() checkpoints.append("bad.cleanup.done:NOT_REACHED") # -- PERFORM TEST: the_fixture1 = None the_fixture2 = None the_fixture3 = None checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_cleanup_error with pytest.raises(FixtureCleanupError): with scoped_context_layer(context): the_fixture1 = use_fixture(foo, context, checkpoints, name="foo_1") the_fixture2 = use_fixture(bad_fixture, context, checkpoints, name="BAD") the_fixture3 = use_fixture(foo, context, checkpoints, name="foo_3") checkpoints.append("scoped-block") # -- VERIFY: Tries to perform all cleanups even when cleanup-error(s) occur. assert checkpoints == [ "foo.setup:foo_1", "bad.setup", "foo.setup:foo_3", "scoped-block", "foo.cleanup:foo_3", "bad.cleanup_with_error", "foo.cleanup:foo_1" ]
def test_with_function(self): @fixture def bar(context, checkpoints, *args, **kwargs): checkpoints.append("bar.setup") fixture_object = BarFixture.setup(*args, **kwargs) context.bar = fixture_object return fixture_object checkpoints = [] context = make_runtime_context() with scoped_context_layer(context): the_fixture = use_fixture(bar, context, checkpoints) assert_context_setup(context, "bar", BarFixture) assert_fixture_setup_called(the_fixture) assert_fixture_cleanup_not_called(the_fixture) assert checkpoints == ["bar.setup"] print("Do something...") assert_context_cleanup(context, "foo") assert_fixture_cleanup_not_called(the_fixture)
def test_bad_with_cleanup_error(self): @fixture def bad_with_cleanup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup") yield FooFixture(*args, **kwargs) checkpoints.append("bad.cleanup_with_error") raise FixtureCleanupError() # -- PERFORM TEST: checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_cleanup_error with pytest.raises(FixtureCleanupError): with scoped_context_layer(context): use_fixture(bad_fixture, context, checkpoints) checkpoints.append("scoped-block") # -- VERIFY: Ensure normal cleanup-parts were performed or tried. assert checkpoints == ["bad.setup", "scoped-block", "bad.cleanup_with_error"]
def test_basic_lifecycle(self): # -- NOTE: Use explicit checks instead of assert-helper functions. @fixture def foo(context, checkpoints, *args, **kwargs): checkpoints.append("foo.setup") yield FooFixture() checkpoints.append("foo.cleanup") checkpoints = [] context = make_runtime_context() with scoped_context_layer(context): the_fixture = use_fixture(foo, context, checkpoints) # -- ENSURE: Fixture setup is performed (and as expected) assert isinstance(the_fixture, FooFixture) assert checkpoints == ["foo.setup"] checkpoints.append("scoped-block") # -- ENSURE: Fixture cleanup was performed assert checkpoints == ["foo.setup", "scoped-block", "foo.cleanup"]
def test_block_eror_with_plaingen_then_cleanup_is_called(self): # -- CASE: Fixture is generator-function @fixture def foo(context, checkpoints): checkpoints.append("foo.setup") yield checkpoints.append("foo.cleanup") checkpoints = [] context = make_runtime_context() with pytest.raises(RuntimeError): with scoped_context_layer(context): use_fixture(foo, context, checkpoints) checkpoints.append("scoped-block_with_error") raise RuntimeError("scoped-block") checkpoints.append("NOT_REACHED") # -- ENSURE: assert checkpoints == [ "foo.setup", "scoped-block_with_error", "foo.cleanup" ]
def test_setup_error_with_context_cleanup1_then_cleanup_is_called(self): # -- CASE: Fixture is normal function @fixture def foo(context, checkpoints): def cleanup_foo(arg=""): checkpoints.append("cleanup_foo:%s" % arg) checkpoints.append("foo.setup_with_error:foo_1") context.add_cleanup(cleanup_foo, "foo_1") raise FixtureSetupError("foo") checkpoints.append("foo.setup.done:NOT_REACHED") checkpoints = [] context = make_runtime_context() with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(foo, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") # -- ENSURE: cleanup_foo() is called (LATE-CLEANUP on scope-exit) assert checkpoints == ["foo.setup_with_error:foo_1", "cleanup_foo:foo_1"]
def test_with_generator_function(self): @fixture def foo(context, checkpoints, *args, **kwargs): checkpoints.append("foo.setup") fixture_object = FooFixture.setup(*args, **kwargs) context.foo = fixture_object yield fixture_object fixture_object.cleanup() checkpoints.append("foo.cleanup.done") checkpoints = [] context = make_runtime_context() with scoped_context_layer(context): the_fixture = use_fixture(foo, context, checkpoints) assert_context_setup(context, "foo", FooFixture) assert_fixture_setup_called(the_fixture) assert_fixture_cleanup_not_called(the_fixture) assert checkpoints == ["foo.setup"] print("Do something...") assert_context_cleanup(context, "foo") assert_fixture_cleanup_called(the_fixture) assert checkpoints == ["foo.setup", "foo.cleanup.done"]
def test_invalid_fixture_function(self): """Test invalid generator function with more than one yield-statement (not a valid fixture/context-manager). """ @fixture def invalid_fixture(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup") yield FooFixture(*args, **kwargs) checkpoints.append("bad.cleanup") yield None # -- SYNDROME HERE: More than one yield-statement # -- PERFORM-TEST: checkpoints = [] context = make_runtime_context() with pytest.raises(InvalidFixtureError): with scoped_context_layer(context): the_fixture = use_fixture(invalid_fixture, context, checkpoints) assert checkpoints == ["bad.setup"] checkpoints.append("scoped-block") # -- VERIFY: Ensure normal cleanup-parts were performed. assert checkpoints == ["bad.setup", "scoped-block", "bad.cleanup"]
def test_use_composite_fixture_with_setup_error(self): @fixture def fixture_foo(context, checkpoints, *args, **kwargs): fixture_name = kwargs.get("name", "foo") checkpoints.append("foo.setup:%s" % fixture_name) yield checkpoints.append("foo.cleanup:%s" % fixture_name) @fixture def bad_with_setup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup_with_error") raise FixtureSetupError("OOPS") yield checkpoints.append("bad.cleanup:NOT_REACHED") @fixture def composite3(context, checkpoints, *args, **kwargs): bad_fixture = bad_with_setup_error the_composite = use_composite_fixture_with(context, [ fixture_call_params(fixture_foo, checkpoints, name="_1"), fixture_call_params(bad_fixture, checkpoints, name="OOPS"), fixture_call_params(fixture_foo, checkpoints, name="_3:NOT_REACHED"), ]) return the_composite # -- PERFORM-TEST: context = make_runtime_context() checkpoints = [] with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(composite3, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") # -- ENSURES: # * fixture1-cleanup is called even after fixture2-setup-error # * fixture3-setup/cleanup are not called due to fixture2-setup-error assert checkpoints == [ "foo.setup:_1", "bad.setup_with_error", "foo.cleanup:_1" ]
def test_setup_eror_with_finallygen_then_cleanup_is_called(self): # -- CASE: Fixture is generator-function @fixture def foo(context, checkpoints): try: checkpoints.append("foo.setup.begin") raise FixtureSetupError("foo") checkpoints.append("foo.setup.done:NOT_REACHED") yield checkpoints.append("foo.cleanup:NOT_REACHED") finally: checkpoints.append("foo.cleanup.finally") checkpoints = [] context = make_runtime_context() with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(foo, context, checkpoints) # -- NEVER-REACHED: checkpoints.append("scoped-block:NOT_REACHED") # -- ENSURE: fixture-cleanup (foo.cleanup) is called (EARLY-CLEANUP). assert checkpoints == ["foo.setup.begin", "foo.cleanup.finally"]
def test_block_eror_with_context_cleanup_then_cleanup_is_called(self): # -- CASE: Fixture is normal-function @fixture def bar(context, checkpoints): def cleanup_bar(): checkpoints.append("cleanup_bar") checkpoints.append("bar.setup") context.add_cleanup(cleanup_bar) checkpoints = [] context = make_runtime_context() with pytest.raises(RuntimeError): with scoped_context_layer(context): use_fixture(bar, context, checkpoints) checkpoints.append("scoped-block_with_error") raise RuntimeError("scoped-block") checkpoints.append("NOT_REACHED") # -- ENSURE: assert checkpoints == [ "bar.setup", "scoped-block_with_error", "cleanup_bar" ]
def test_data_schema1(self): @fixture def foo(context, *args, **kwargs): # -- NOTE checkpoints: Injected from outer scope. checkpoints.append("foo.setup") yield "fixture:foo" checkpoints.append("foo.cleanup") fixture_registry = { "fixture.foo": foo, } # -- PERFORM-TEST: context = make_runtime_context() checkpoints = [] with scoped_context_layer(context): use_fixture_by_tag("fixture.foo", context, fixture_registry) checkpoints.append("scoped-block") # -- VERIFY: assert checkpoints == [ "foo.setup", "scoped-block", "foo.cleanup" ]
def test_bad_with_setup_error(self): # -- SAD: cleanup_fixture() part is called when setup-error occurs, # but not the cleanup-part of the generator (generator-magic). @fixture def bad_with_setup_error(context, checkpoints, *args, **kwargs): checkpoints.append("bad.setup_with_error") raise FixtureSetupError() yield FooFixture(*args, **kwargs) checkpoints.append("bad.cleanup:NOT_REACHED") # -- PERFORM-TEST: the_fixture = None checkpoints = [] context = make_runtime_context() bad_fixture = bad_with_setup_error with pytest.raises(FixtureSetupError): with scoped_context_layer(context): the_fixture = use_fixture(bad_fixture, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") # -- VERIFY: Ensure normal cleanup-parts were performed. # * SAD: fixture-cleanup is not called due to fixture-setup error. assert the_fixture is None # -- NEVER STORED: Due to setup error. assert checkpoints == ["bad.setup_with_error"]
def test_setup_error_with_context_cleanup2_then_cleanup_is_called(self): # -- CASE: Fixture is generator-function (contextmanager) # NOTE: Explicit use of context.add_cleanup() @fixture def foo(context, checkpoints, **kwargs): def cleanup_foo(arg=""): checkpoints.append("cleanup_foo:%s" % arg) checkpoints.append("foo.setup_with_error:foo_1") context.add_cleanup(cleanup_foo, "foo_1") raise FixtureSetupError("foo_1") checkpoints.append("foo.setup.done:NOT_REACHED") yield checkpoints.append("foo.cleanup:NOT_REACHED") checkpoints = [] context = make_runtime_context() with pytest.raises(FixtureSetupError): with scoped_context_layer(context): use_fixture(foo, context, checkpoints) checkpoints.append("scoped-block:NOT_REACHED") # -- ENSURE: cleanup_foo() is called assert checkpoints == ["foo.setup_with_error:foo_1", "cleanup_foo:foo_1"]
def test_issue__getattr_with_protected_unknown_context_attribute_raises_no_error(): context = Context(runner=Mock()) with scoped_context_layer(context): # CALLS-HERE: context._push() value = getattr(context, "_UNKNOWN_ATTRIB", "__UNKNOWN__") assert value == "__UNKNOWN__"