def test_run_debug_step_function_with_exception(debug_or_run, mocker, mock_utils_debugger): """ Test running/debugging a Step which raises an Exception """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) step.definition_func = StepHelper.step_fail_func step.argument_match = mocker.MagicMock() step.argument_match.evaluate.return_value = (tuple(), {}) # when method = getattr(step, debug_or_run) state = method() # then assert state == step.state == Step.State.FAILED assert step.failure is not None assert step.failure.reason == "failing step" assert step.failure.name == "AssertionError"
def test_run_debug_step_function_mark_skipped(debug_or_run, mocker, mock_utils_debugger): """ Test running/debugging a Step which marks itself as skipped """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) step.definition_func = StepHelper.step_skip_func step.argument_match = mocker.MagicMock() step.argument_match.evaluate.return_value = (tuple(), {}) # when method = getattr(step, debug_or_run) state = method() # then assert state == Step.State.SKIPPED == step.state
def test_run_debug_step_function_mark_skipped( debug_or_run, mocker, mock_utils_debugger ): """ Test running/debugging a Step which marks itself as skipped """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) step.definition_func = StepHelper.step_skip_func step.argument_match = mocker.MagicMock() step.argument_match.evaluate.return_value = (tuple(), {}) # when method = getattr(step, debug_or_run) state = method() # then assert state == Step.State.SKIPPED == step.state
def test_running_a_feature(self): """ Test running a feature """ data = threading.local() data.step_was_called = False def some_step(step): data.step_was_called = True feature = Feature(1, "Feature", "Some feature", "somefile.feature", 1) scenario = Scenario(1, 1, "Scenario", "Some scenario", "somefile.feature", 2, feature) feature.scenarios.append(scenario) step = Step(1, "Some step", "somefile.feature", 3, scenario, True) step.definition_func = some_step argument_match_mock = Mock() argument_match_mock.evaluate.return_value = (tuple(), {}) step.argument_match = argument_match_mock scenario.steps.append(step) hook_mock = Mock() hook_mock.call.return_value = True runner = Runner(hook_mock) runner.run_feature(feature) step.state.should.be.equal(Step.State.PASSED) data.step_was_called.should.be.true
def test_run_step_with_keyword_arguments_passed(self): """ Test running a passing step with keyword arguments """ data = threading.local() data.step_was_run = False data.number = None data.string = None def step_passed(step, number, string): data.step_was_run = True data.number = int(number) data.string = string step = Step(1, "I call a passing step with string argument 'Tschau' and number argument 42", "somefile.feature", 3, None, True) step.definition_func = step_passed match = re.search("I call a passing step with string argument '(?P<string>.*?)' and number argument (?P<number>\d+)", step.sentence) step.arguments = match.groups() step.keyword_arguments = match.groupdict() step.state.should.be.equal(Step.State.UNTESTED) step.run.when.called_with().should.return_value(Step.State.PASSED) data.step_was_run.should.be.true data.number.should.be.equal(42) data.string.should.be.equal("Tschau")
def test_run_debug_step_function_with_posargs( debug_or_run, mocker, mock_utils_debugger ): """ Test running/debugging a Step with a function and positional arguments """ # mock step function which is to use mocker.spy(StepHelper, "step_func") # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) step.definition_func = StepHelper.step_func step.argument_match = mocker.MagicMock() step.argument_match.evaluate.return_value = ((1, 2), {}) # when method = getattr(step, debug_or_run) state = method() # then assert state == Step.State.PASSED StepHelper.step_func.assert_called_once_with(step, 1, 2)
def test_after_each_step_failed(self): """ Test after.each_step from extension console_writer with failed step """ data = threading.local() data.console = None def patched_write(text): text = re.sub(r"\x1b[^m]*m", "", text) data.console = text scenario_mock = Mock() scenario_mock.parent = None step = Step(1, "I test the console writer", "somefile.feature", 3, scenario_mock, False) step.parent = MagicMock() step.parent.id = 1 step.parent.parent = Mock(spec=Feature) step.parent.parent.id = 1 step.state = step.State.FAILED try: assert False, "Some assertion happend" except AssertionError as e: step.failure = utils.Failure(e) with patch("radish.extensions.console_writer.write", side_effect=patched_write): HookRegistry().call("after", "each_step", step) data.console.should.be.equal("""\rI test the console writer AssertionError: Some assertion happend""")
def test_running_all(self): """ Test running a all features """ data = threading.local() data.step_was_called = False def some_step(step): data.step_was_called = True feature = Feature(1, "Feature", "Some feature", "somefile.feature", 1) scenario = Scenario(1, 1, "Scenario", "Some scenario", "somefile.feature", 2, feature) feature.scenarios.append(scenario) step = Step(1, "Some step", "somefile.feature", 3, scenario, True) step.definition_func = some_step step.arguments = tuple() step.keyword_arguments = {} scenario.steps.append(step) hook_mock = Mock() hook_mock.call.return_value = True runner = Runner(hook_mock) runner.start([feature], None) step.state.should.be.equal(Step.State.PASSED) data.step_was_called.should.be.true
def test_running_a_feature(self): """ Test running a feature """ data = threading.local() data.step_was_called = False def some_step(step): data.step_was_called = True feature = Feature(1, "Feature", "Some feature", "somefile.feature", 1) scenario = Scenario(1, 1, "Scenario", "Some scenario", "somefile.feature", 2, feature) feature.scenarios.append(scenario) step = Step(1, "Some step", "somefile.feature", 3, scenario, True) step.definition_func = some_step step.arguments = tuple() step.keyword_arguments = {} scenario.steps.append(step) hook_mock = Mock() hook_mock.call.return_value = True runner = Runner(hook_mock) runner.run_feature(feature) step.state.should.be.equal(Step.State.PASSED) data.step_was_called.should.be.true
def test_after_each_step(self): """ Test after.each_step from extension console_writer """ data = threading.local() data.console = None def patched_write(text): text = re.sub(r"\x1b[^m]*m", "", text) data.console = text scenario_mock = Mock() scenario_mock.parent = None step = Step(1, "I test the console writer", "somefile.feature", 3, scenario_mock, False) step.parent = Mock() step.parent.id = 1 step.parent.parent = Mock(spec=Feature) step.parent.parent.id = 1 with patch("radish.extensions.console_writer.write", side_effect=patched_write): HookRegistry().call("after", "each_step", step) data.console.should.be.equal("\rI test the console writer")
def test_running_a_scenario(self): """ Test running a scenario """ data = threading.local() data.step_was_called = False def some_step(step): data.step_was_called = True step = Step(1, "Some step", "somefile.feature", 3, None, True) step.definition_func = some_step step.arguments = tuple() step.keyword_arguments = {} scenario = Scenario(1, 1, "Scenario", "Some scenario", "somefile.feature", 2, None) scenario.steps.append(step) hook_mock = Mock() hook_mock.call.return_value = True runner = Runner(hook_mock) returncode = runner.run_scenario(scenario) returncode.should.be.equal(0) step.state.should.be.equal(Step.State.PASSED) data.step_was_called.should.be.true
def test_merge_steps(self): """ Test merging steps from feature files with registered steps """ matcher = Matcher() steps = { re.compile(r"Given I have the number (\d+)"): "some_func", re.compile(r"I add (\d+) to my number"): "some_other_func" } feature = Feature(1, "Feature", "Some feature", "test.feature", 1) scenario = Scenario(1, "Scenario", "Adding numbers", "test.feature", 2, feature) scenario.steps.append( Step(1, "Given I have the number 5", "test.feature", 3, scenario, False)) scenario.steps.append( Step(2, "When I add 2 to my number", "test.feature", 4, scenario, False)) feature.scenarios.append(scenario) matcher.merge_steps([feature], steps) scenario.steps[0].definition_func.should.be.equal("some_func") scenario.steps[0].arguments.should.be.equal(("5", )) scenario.steps[1].definition_func.should.be.equal("some_other_func") scenario.steps[1].arguments.should.be.equal(("2", ))
def test_run_debug_step_function_with_posargs(debug_or_run, mocker, mock_utils_debugger): """ Test running/debugging a Step with a function and positional arguments """ # mock step function which is to use mocker.spy(StepHelper, "step_func") # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) step.definition_func = StepHelper.step_func step.argument_match = mocker.MagicMock() step.argument_match.evaluate.return_value = ((1, 2), {}) # when method = getattr(step, debug_or_run) state = method() # then assert state == Step.State.PASSED StepHelper.step_func.assert_called_once_with(step, 1, 2)
def test_run_debug_step_function_with_exception( debug_or_run, mocker, mock_utils_debugger ): """ Test running/debugging a Step which raises an Exception """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) step.definition_func = StepHelper.step_fail_func step.argument_match = mocker.MagicMock() step.argument_match.evaluate.return_value = (tuple(), {}) # when method = getattr(step, debug_or_run) state = method() # then assert state == step.state == Step.State.FAILED assert step.failure is not None assert step.failure.reason == "failing step" assert step.failure.name == "AssertionError"
def test_skip_a_step(): """ Test skipping a Step """ # given step = Step(1, 'I am a Step', 'foo.feature', 1, parent=None, runable=True, context_class=None) # when step.skip() # then assert step.state == Step.State.SKIPPED
def test_getting_raw_text_from_step(given_raw_text, expected_text): """ Test getting a Step's raw text data """ # given step = Step(1, 'I am a Step', 'foo.feature', 1, parent=None, runable=True, context_class=None) step.raw_text = given_raw_text # when text = step.text # then assert text == expected_text
def test_building_scenariooutline_scenarios_with_background(mocker): """ Test building Scenarios from a Scenario Outline Example including a Background """ # given background = Background( "Background", "I am a Background", "foo.feature", 1, parent=None ) # add some Steps background.steps.extend( [ Step(1, "Foo", "foo.feature", 2, background, False), Step(2, "Foo", "foo.feature", 3, background, False), ] ) scenario_outline = ScenarioOutline( 1, "Scenario Outline", "Examples", "I am a Scenario Outline", "foo.feature", 1, parent=None, tags=None, preconditions=None, background=background, ) # add steps scenario_outline.steps.extend( [ mocker.MagicMock(sentence="Given I have <foo>", path="foo.feature"), mocker.MagicMock(sentence="And I have <bar>", path="foo.feature"), mocker.MagicMock(sentence="When I add those", path="foo.feature"), ] ) # add examples scenario_outline.examples_header = ["foo", "bar"] scenario_outline.examples = [ # row 0 ScenarioOutline.Example(["1", "2"], "foo.feature", 1), # row 3 ScenarioOutline.Example(["3", "4"], "foo.feature", 2), ] # when - build the scenarios scenario_outline.build_scenarios() # then - expect ExampleScenarios to have background copy assigned assert scenario_outline.scenarios[0].background.sentence == "I am a Background" assert scenario_outline.scenarios[1].background.sentence == "I am a Background"
def test_building_scenariooutline_scenarios_with_background(mocker): """ Test building Scenarios from a Scenario Outline Example including a Background """ # given background = Background('Background', 'I am a Background', 'foo.feature', 1, parent=None) # add some Steps background.steps.extend([ Step(1, 'Foo', 'foo.feature', 2, background, False), Step(2, 'Foo', 'foo.feature', 3, background, False) ]) scenario_outline = ScenarioOutline(1, 'Scenario Outline', 'Examples', 'I am a Scenario Outline', 'foo.feature', 1, parent=None, tags=None, preconditions=None, background=background) # add steps scenario_outline.steps.extend([ mocker.MagicMock(sentence='Given I have <foo>', path='foo.feature'), mocker.MagicMock(sentence='And I have <bar>', path='foo.feature'), mocker.MagicMock(sentence='When I add those', path='foo.feature') ]) # add examples scenario_outline.examples_header = ['foo', 'bar'] scenario_outline.examples = [ # row 0 ScenarioOutline.Example(['1', '2'], 'foo.feature', 1), # row 3 ScenarioOutline.Example(['3', '4'], 'foo.feature', 2), ] # when - build the scenarios scenario_outline.build_scenarios() # then - expect ExampleScenarios to have background copy assigned assert scenario_outline.scenarios[ 0].background.sentence == 'I am a Background' assert scenario_outline.scenarios[ 1].background.sentence == 'I am a Background'
def test_run_step_passed(self): """ Test running a passing step """ data = threading.local() data.step_was_run = False def step_passed(step): data.step_was_run = True step = Step(1, "I call a passing step", "somefile.feature", 3, None, True) step.definition_func = step_passed step.arguments = re.search(step.sentence, step.sentence).groups() step.state.should.be.equal(Step.State.UNTESTED) step.run.when.called_with().should.return_value(Step.State.PASSED) data.step_was_run.should.be.true
def test_run_step_with_invalid_defintion_func(): """ Test running a Step with an invalid definition function """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) # when the Step doesn't have a definition function with pytest.raises(RadishError) as exc: step.run() # then the Step fails with an Exception assert str( exc.value) == "The step 'I am a Step' does not have a step definition" # when the Step has a non-callable definition function step.definition_func = "not-callable" with pytest.raises(RadishError) as exc: step.run() # then the step fails with an Exception assert str( exc.value) == "The step 'I am a Step' does not have a step definition"
def test_building_scenarioloop_scenarios_with_background(mocker): """ Test building Scenarios from a Scenario Loop including a Background """ # given background = Background("Background", "I am a Background", "foo.feature", 1, parent=None) # add some Steps background.steps.extend([ Step(1, "Foo", "foo.feature", 2, background, False), Step(2, "Foo", "foo.feature", 3, background, False), ]) scenario_loop = ScenarioLoop( 1, "Scenario Loop", "Iterations", "I am a Scenario Loop", "foo.feature", 1, parent=None, tags=None, preconditions=None, background=background, ) # add steps scenario_loop.steps.extend([ mocker.MagicMock(sentence="Given I have 1", path="foo.feature"), mocker.MagicMock(sentence="And I have 2", path="foo.feature"), mocker.MagicMock(sentence="When I add those", path="foo.feature"), ]) # set iterations scenario_loop.iterations = 2 # when - build the scenarios scenario_loop.build_scenarios() # then - expect ExampleScenarios to have background copy assigned assert scenario_loop.scenarios[ 0].background.sentence == "I am a Background" assert scenario_loop.scenarios[ 1].background.sentence == "I am a Background"
def test_building_scenarioloop_scenarios_with_background(mocker): """ Test building Scenarios from a Scenario Loop including a Background """ # given background = Background('Background', 'I am a Background', 'foo.feature', 1, parent=None) # add some Steps background.steps.extend([ Step(1, 'Foo', 'foo.feature', 2, background, False), Step(2, 'Foo', 'foo.feature', 3, background, False) ]) scenario_loop = ScenarioLoop(1, 'Scenario Loop', 'Iterations', 'I am a Scenario Loop', 'foo.feature', 1, parent=None, tags=None, preconditions=None, background=background) # add steps scenario_loop.steps.extend([ mocker.MagicMock(sentence='Given I have 1', path='foo.feature'), mocker.MagicMock(sentence='And I have 2', path='foo.feature'), mocker.MagicMock(sentence='When I add those', path='foo.feature') ]) # set iterations scenario_loop.iterations = 2 # when - build the scenarios scenario_loop.build_scenarios() # then - expect ExampleScenarios to have background copy assigned assert scenario_loop.scenarios[ 0].background.sentence == 'I am a Background' assert scenario_loop.scenarios[ 1].background.sentence == 'I am a Background'
def test_run_debug_step_function_with_kwargs(debug_or_run, mocker, mock_utils_debugger): """ Test running/debugging a Step with a function and keyword arguments """ # mock step function which is to use mocker.spy(StepHelper, 'step_func') # given step = Step(1, 'I am a Step', 'foo.feature', 1, parent=None, runable=True, context_class=None) step.definition_func = StepHelper.step_func step.argument_match = mocker.MagicMock() step.argument_match.evaluate.return_value = (tuple(), {'foo': '1', 'bar': '2'}) # when method = getattr(step, debug_or_run) state = method() # then assert state == Step.State.PASSED StepHelper.step_func.assert_called_once_with(step, foo='1', bar='2')
def test_skip_a_step(): """ Test skipping a Step """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) # when step.skip() # then assert step.state == Step.State.SKIPPED
def test_run_step_failed(self): """ Test running a failing step """ data = threading.local() data.step_was_run = False def step_failed(step): data.step_was_run = True assert False, "This step fails by design" step = Step(1, "I call a failing step", "somefile.feature", 3, None, True) step.definition_func = step_failed step.arguments = re.search(step.sentence, step.sentence).groups() step.state.should.be.equal(Step.State.UNTESTED) step.run.when.called_with().should.return_value(Step.State.FAILED) step.failure.shouldnt.be.none step.failure.reason.should.be.equal("This step fails by design") data.step_was_run.should.be.true
def test_merge_non_existing_step(self): """ Test merging non existing step """ steps = {re.compile(r"Given I have the number (\d+)"): "some_func", re.compile(r"I add (\d+) to my number"): "some_other_func"} feature = Feature(1, "Feature", "Some feature", "test.feature", 1) scenario = Scenario(1, "Scenario", "Adding numbers", "test.feature", 2, feature) scenario.steps.append(Step(1, "When I call a non-existing step", "test.feature", 3, scenario, False)) feature.scenarios.append(scenario) merge_steps.when.called_with([feature], steps).should.throw(StepDefinitionNotFoundError, "Cannot find step definition for step 'When I call a non-existing step' in test.feature:3")
def test_getting_raw_text_from_step(given_raw_text, expected_text): """ Test getting a Step's raw text data """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) step.raw_text = given_raw_text # when text = step.text # then assert text == expected_text
def test_run_step_with_invalid_defintion_func(): """ Test running a Step with an invalid definition function """ # given step = Step( 1, "I am a Step", "foo.feature", 1, parent=None, runable=True, context_class=None, ) # when the Step doesn't have a definition function with pytest.raises(RadishError) as exc: step.run() # then the Step fails with an Exception assert str(exc.value) == "The step 'I am a Step' does not have a step definition" # when the Step has a non-callable definition function step.definition_func = "not-callable" with pytest.raises(RadishError) as exc: step.run() # then the step fails with an Exception assert str(exc.value) == "The step 'I am a Step' does not have a step definition"
def test_running_a_step(self): """ Test running a step """ data = threading.local() data.step_was_called = False def some_step(step): data.step_was_called = True step = Step(1, "Some step", "somefile.feature", 3, None, True) step.definition_func = some_step step.arguments = tuple() step.keyword_arguments = {} hook_mock = Mock() hook_mock.call.return_value = True runner = Runner(hook_mock) returncode = runner.run_step(step) returncode.should.be.equal(0) step.state.should.be.equal(Step.State.PASSED) data.step_was_called.should.be.true
def test_run_not_runable_step(debug_or_run, mock_utils_debugger): """ Test running a non-runable Step """ # given step = Step(1, 'I am a Step', 'foo.feature', 1, parent=None, runable=False, context_class=None) # when method = getattr(step, debug_or_run) state = method() # then assert state == Step.State.UNTESTED
def test_getting_context_sensitive_sentence(mocker): """ Test getting the context sensitive Step sentence """ # given scenario = mocker.MagicMock(constants=[ ('foo', '42'), ('bar', '21'), ('bla', '33') ]) step = Step(1, 'And I am ${foo} and ${bar} bla', 'foo.feature', 1, parent=scenario, runable=True, context_class='Given') step_no_context = Step(1, 'And I am ${foo} and ${bar} bla', 'foo.feature', 1, parent=scenario, runable=True, context_class=None) # when sentence = step.context_sensitive_sentence sentence_no_context = step_no_context.context_sensitive_sentence # then assert sentence == 'Given I am 42 and 21 bla' assert step.sentence == 'And I am ${foo} and ${bar} bla' assert sentence_no_context == 'And I am 42 and 21 bla' assert step_no_context.sentence == 'And I am ${foo} and ${bar} bla'
def test_creating_a_concrete_background_instance(): """ Test creating a concrete Background instance """ # given & when background = Background("Background", "I am a Background", "foo.feature", 1, parent=None) # add some Steps background.steps.append(Step(1, "Foo", "foo.feature", 2, background, False)) background.steps.append(Step(2, "Foo", "foo.feature", 3, background, False)) # when instance = background.create_instance() # then assert len(instance.steps) == 2 assert background.steps[0] != instance.steps[0] assert background.steps[1] != instance.steps[1]
def test_running_a_step(self): """ Test running a step """ data = threading.local() data.step_was_called = False def some_step(step): data.step_was_called = True step = Step(1, "Some step", "somefile.feature", 3, None, True) step.definition_func = some_step argument_match_mock = Mock() argument_match_mock.evaluate.return_value = (tuple(), {}) step.argument_match = argument_match_mock hook_mock = Mock() hook_mock.call.return_value = True runner = Runner(hook_mock) returncode = runner.run_step(step) returncode.should.be.equal(0) step.state.should.be.equal(Step.State.PASSED) data.step_was_called.should.be.true
def test_getting_step_context_object(mocker): """ Test getting a Step's context object """ # given scenario = mocker.MagicMock(context=42) step = Step(1, 'I am a Step', 'foo.feature', 1, parent=scenario, runable=True, context_class=None) # when context = step.context # then assert context == 42 assert context == scenario.context
def test_getting_context_sensitive_sentence(mocker): """ Test getting the context sensitive Step sentence """ # given scenario = mocker.MagicMock(constants=[("foo", "42"), ("bar", "21"), ("bla", "33")]) step = Step( 1, "And I am ${foo} and ${bar} bla", "foo.feature", 1, parent=scenario, runable=True, context_class="Given", ) step_no_context = Step( 1, "And I am ${foo} and ${bar} bla", "foo.feature", 1, parent=scenario, runable=True, context_class=None, ) # when sentence = step.context_sensitive_sentence sentence_no_context = step_no_context.context_sensitive_sentence # then assert sentence == "Given I am 42 and 21 bla" assert step.sentence == "And I am ${foo} and ${bar} bla" assert sentence_no_context == "And I am 42 and 21 bla" assert step_no_context.sentence == "And I am ${foo} and ${bar} bla"
def test_returncode_of_runner(self): """ Test returncode of run functions in Runner """ def some_passed_step(step): pass def some_failed_step(step): raise AttributeError("I expect this error to test the behavior of a failed step") feature = Feature(1, "Feature", "Some feature", "somefile.feature", 1) scenario = Scenario(1, 1, "Scenario", "Some scenario", "somefile.feature", 2, feature) feature.scenarios.append(scenario) step1 = Step(1, "Some passed step", "somefile.feature", 3, scenario, True) step1.definition_func = some_passed_step step1.arguments = tuple() step1.keyword_arguments = {} scenario.steps.append(step1) step2 = Step(2, "Some failed step", "somefile.feature", 4, scenario, True) step2.definition_func = some_failed_step step2.arguments = tuple() step2.keyword_arguments = {} scenario.steps.append(step2) hook_mock = Mock() hook_mock.call.return_value = True runner = Runner(hook_mock) returncode = runner.run_feature(feature) returncode.should.be.equal(1) step1.state.should.be.equal(Step.State.PASSED) step2.state.should.be.equal(Step.State.FAILED) scenario.state.should.be.equal(Step.State.FAILED) feature.state.should.be.equal(Step.State.FAILED)
def test_creating_simple_step(): """ Test creating a simple Step """ # given & when step = Step(1, 'I am a Step', 'foo.feature', 1, parent=None, runable=True, context_class=None) # then assert step.id == 1 assert step.sentence == 'I am a Step' assert step.path == 'foo.feature' assert step.line == 1 assert step.parent is None assert step.runable is True assert step.context_class is None
def test_getting_expanded_sentence(mocker): """ Test getting the expanded Step sentence """ # given scenario = mocker.MagicMock(constants=[ ('foo', '42'), ('bar', '21'), ('bla', '33') ]) step = Step(1, 'I am ${foo} and ${bar} bla', 'foo.feature', 1, parent=scenario, runable=True, context_class=None) # when sentence = step.expanded_sentence # then assert sentence == 'I am 42 and 21 bla' assert step.sentence == 'I am ${foo} and ${bar} bla'