package net.thucydides.core.steps; import net.thucydides.core.annotations.*; import net.thucydides.core.model.TestOutcome; import net.thucydides.core.pages.Pages; import net.thucydides.core.steps.samples.SomeTestScenario; import net.thucydides.core.util.ExtendedTemporaryFolder; import org.junit.*; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriverException; import java.io.IOException; import java.util.Arrays; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; public class WhenRunningStepsThroughAScenarioProxy { static class NestedTestScenarioSteps extends ScenarioSteps { public NestedTestScenarioSteps(Pages pages) { super(pages); } @Steps NestedNestedTestScenarioSteps nestedNestedTestScenarioSteps; @Step("Step group 1") public void step_group1(){ step1(); step2(); step3(); } @Step public void step1(){ getDriver().get("nested.step_one"); } @Step public void step2(){ getDriver().get("nested.step_two"); } @Step public void step3(){ getDriver().get("nested.step_three"); } @Ignore @Step public void ignored_step() { getDriver().get("nested.ignored_step"); } @Step public void nested_steps() { nestedNestedTestScenarioSteps.step1(); } @Step public void failing_step() { throw new AssertionError("Deliberate failure"); } @Step(callNestedMethods=false) public void shouldnt_call_nested_methods_after_failure() { getDriver().get("nested.method"); } } static class NestedNestedTestScenarioSteps extends ScenarioSteps { public NestedNestedTestScenarioSteps(Pages pages) { super(pages); } @Step public void step1(){ getDriver().get("nested.nested.step_one"); } } static class SimpleTestScenarioSteps extends ScenarioSteps { @Steps public NestedTestScenarioSteps nestedSteps; public SimpleTestScenarioSteps(Pages pages) { super(pages); } @StepGroup("Step group 1") public void step_group1(){ step_one(); step2(); step3(); } @StepGroup public void step_group_with_failure(){ throw new AssertionError("Oh bother!"); } @Step public void step_one(){ getDriver().get("step_one"); } @Step public void step2(){ getDriver().get("step_two"); } @Step public void step3(){ getDriver().get("step_three"); } @Step public void step_with_parameter(String name){ getDriver().get("step_with_parameter"); } @Step public void step_with_parameters(String name, int age){ getDriver().get("step_with_parameters"); } @Step public void step_with_parameters(List<Integer> numbers){ getDriver().get("step_with_parameters"); } @Step public void step_with_array(Integer[] numbers){ getDriver().get("step_with_array"); } @Ignore @Step public void ignored_step(){ getDriver().get("ignored_step"); } @Pending @Step public void pending_step(){ getDriver().get("pending_step"); } @Step public void failing_step() { getDriver().get("failing_step"); throw new AssertionError("Oops!"); } @Step public void failing_web_step() { getDriver().get("failing_step"); throw new WebDriverException("Oops!", new WebDriverException("Element not found")); } @Step public void no_such_element_web_step() { getDriver().get("failing_step"); throw new NoSuchElementException("Could not find element"); } @Step public void step_with_failing_ordinary_method() { failing_ordinary_method(); } @Step public void step_with_failing_web_method() { failing_ordinary_web_method(); } public void failing_ordinary_method() { throw new AssertionError("Oops!"); } public void failing_ordinary_web_method() { throw new WebDriverException("Oops!"); } @Step public void nested_steps() { getDriver().get("nested_steps"); nestedSteps.step1(); nestedSteps.step2(); nestedSteps.step3(); nestedSteps.nested_steps(); } @Step public void nested_steps_with_ignored_steps() { getDriver().get("nested_steps_with_ignored_steps"); nestedSteps.step1(); nestedSteps.step2(); nestedSteps.step3(); nestedSteps.ignored_step(); } } static class DatabaseAPI { public void call(String param) {} } static class NestedDatabaseScenarioSteps extends ScenarioSteps { public DatabaseAPI databaseAPI; public NestedDatabaseScenarioSteps(Pages pages) { super(pages); } @Steps NestedNestedTestScenarioSteps nestedNestedTestScenarioSteps; @Step public void steps_without_failure(){ step1(); step2(); shouldnt_call_nested_methods_after_failure(); } @Step public void steps_with_failure(){ step1(); failing_step(); shouldnt_call_nested_methods_after_failure(); } @Step public void steps_with_error(){ step1(); step_with_error(); shouldnt_call_nested_methods_after_failure(); } @Step public void steps_with_ignored_step(){ step1(); ignored_step(); shouldnt_call_nested_methods_after_failure(); } @Step public void step1(){ databaseAPI.call("nested.step_one"); } @Step public void step2(){ databaseAPI.call("nested.step_two"); } @Step public void step3(){ databaseAPI.call("nested.step_three"); } @Step public void failing_step() { throw new AssertionError("Deliberate failure"); } @Step public void step_with_error() { throw new NullPointerException("Deliberate failure"); } @Step @Ignore public void ignored_step() { throw new NullPointerException("Deliberate failure"); } @Step(callNestedMethods=false) public void shouldnt_call_nested_methods_after_failure() { databaseAPI.call("nested.method"); } } static class SimpleTestScenarioStepsWithNestedCallsDisabled extends ScenarioSteps { @Steps public NestedTestScenarioSteps nestedSteps; DatabaseAPI databaseAPI; public SimpleTestScenarioStepsWithNestedCallsDisabled(Pages pages) { super(pages); } @Step public void steps_with_failure(){ nestedSteps.step1(); nestedSteps.failing_step(); nestedSteps.shouldnt_call_nested_methods_after_failure(); } @Step public void steps_without_failure(){ nestedSteps.step1(); nestedSteps.step2(); nestedSteps.shouldnt_call_nested_methods_after_failure(); } } @Mock WebDriver driver; @Mock StepListener listener; BaseStepListener baseStepListener; @Rule public ExtendedTemporaryFolder temp = new ExtendedTemporaryFolder(); @Mock TestOutcome testOutcome; private StepFactory factory; @Before public void initMocks() throws IOException { MockitoAnnotations.initMocks(this); factory = new StepFactory(new Pages(driver)); StepEventBus.getEventBus().clear(); baseStepListener = new BaseStepListener(temp.newFolder()); StepEventBus.getEventBus().registerListener(baseStepListener); StepEventBus.getEventBus().registerListener(listener); StepEventBus.getEventBus().testSuiteStarted(SomeTestScenario.class); StepEventBus.getEventBus().testStarted("should_do_something"); } @After public void deregisterListener() { StepEventBus.getEventBus().dropListener(listener); } @Test public void the_proxy_should_execute_steps_transparently() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_one(); steps.step2(); steps.step3(); verify(driver).get("step_one"); verify(driver).get("step_two"); verify(driver).get("step_three"); } @Test public void the_proxy_should_store_step_method_parameters() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_with_parameter("Joe"); ArgumentCaptor<ExecutedStepDescription> argument = ArgumentCaptor.forClass(ExecutedStepDescription.class); verify(listener).stepStarted(argument.capture()); assertThat(argument.getValue().getName(), is("step_with_parameter: <span class='step-parameter'>Joe</span>")); } @Test public void the_proxy_should_store_multiple_step_method_parameters() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_with_parameters("Joe", 10); ArgumentCaptor<ExecutedStepDescription> argument = ArgumentCaptor.forClass(ExecutedStepDescription.class); verify(listener).stepStarted(argument.capture()); assertThat(argument.getValue().getName(), is("step_with_parameters: <span class='step-parameter'>Joe, 10</span>")); } @Test public void the_proxy_should_store_list_parameters_in_human_readable_form() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); List<Integer> numbers = Arrays.asList(1,2,3); steps.step_with_parameters(numbers); ArgumentCaptor<ExecutedStepDescription> argument = ArgumentCaptor.forClass(ExecutedStepDescription.class); verify(listener).stepStarted(argument.capture()); assertThat(argument.getValue().getName(), is("step_with_parameters: <span class='step-parameter'>[1, 2, 3]</span>")); } @Test public void the_proxy_should_store_array_parameters_in_human_readable_form() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); Integer[] numbers = {1,2,3}; steps.step_with_array(numbers); ArgumentCaptor<ExecutedStepDescription> argument = ArgumentCaptor.forClass(ExecutedStepDescription.class); verify(listener).stepStarted(argument.capture()); assertThat(argument.getValue().getName(), is("step_with_array: <span class='step-parameter'>{1,2,3}</span>")); } @Test public void the_proxy_should_notify_listeners_when_tests_are_starting() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_one(); steps.step2(); steps.step3(); verify(listener, times(3)).stepStarted(any(ExecutedStepDescription.class)); } class AStory {} @Story(AStory.class) class ATestCase { public void app_should_work() {} } @Test public void the_proxy_should_notify_listeners_when_tests_are_starting_with_details_about_step_name_and_class() { ArgumentCaptor<ExecutedStepDescription> argument = ArgumentCaptor.forClass(ExecutedStepDescription.class); SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); listener.testSuiteStarted(ATestCase.class); listener.testStarted("app_should_work"); steps.step_one(); verify(listener).stepStarted(argument.capture()); assertThat(argument.getValue().getStepClass().getName(), is(SimpleTestScenarioSteps.class.getName())); assertThat(argument.getValue().getName(), is("step_one")); } @Test public void the_proxy_should_notify_listeners_when_tests_have_finished() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_one(); steps.step2(); steps.step3(); verify(listener, times(3)).stepFinished(); } @Test public void the_proxy_should_notify_listeners_when_tests_have_finished_with_description_details() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_one(); verify(listener).stepFinished(); } @Test public void the_proxy_should_notify_listeners_when_test_groups_start_and_finish() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_group1(); verify(listener, times(4)).stepStarted(any(ExecutedStepDescription.class)); verify(listener, times(4)).stepFinished(); } @Test public void the_proxy_should_execute_ignored_steps_but_disable_webdriver() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_one(); steps.ignored_step(); steps.step3(); verify(driver).get("step_one"); verify(driver).get("ignored_step"); verify(driver).get("step_three"); } @Test public void the_proxy_should_skip_tests_after_a_failure() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_one(); steps.failing_step(); steps.step3(); verify(driver).get("step_one"); verify(driver, never()).get("step4"); } @Test public void the_proxy_should_skip_pending_tests() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_one(); steps.ignored_step(); steps.step3(); verify(driver).get("step_one"); verify(driver, never()).get("pending_step"); verify(driver).get("step_three"); } @Test public void the_proxy_should_notify_listeners_of_ignored_tests_as_skipped() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.ignored_step(); verify(listener, times(1)).stepIgnored(); } @Test public void the_proxy_should_notify_listeners_of_pending_tests_as_pending() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.pending_step(); verify(listener, times(1)).stepPending(); } @Test public void the_proxy_should_notify_listeners_when_a_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.failing_step(); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_with_a_description_and_a_cause_when_a_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.failing_step(); ArgumentCaptor<StepFailure> argument = ArgumentCaptor.forClass(StepFailure.class); verify(listener).stepFailed(argument.capture()); assertThat(argument.getValue().getDescription().getStepClass().getName(), is(SimpleTestScenarioSteps.class.getName())); assertThat(argument.getValue().getDescription().getName(), is("failing_step")); assertThat(argument.getValue().getException().getClass().getName(), is(AssertionError.class.getName())); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_when_a_failure_occurs_in_a_group() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_group_with_failure(); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_with_a_description_and_a_cause_when_a_failure_occurs_in_a_group() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_group_with_failure(); ArgumentCaptor<StepFailure> argument = ArgumentCaptor.forClass(StepFailure.class); verify(listener).stepFailed(argument.capture()); assertThat(argument.getValue().getDescription().getStepClass().getName(), is(SimpleTestScenarioSteps.class.getName())); assertThat(argument.getValue().getDescription().getName(), is("step_group_with_failure")); assertThat(argument.getValue().getException().getClass().getName(), is(AssertionError.class.getName())); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_when_a_web_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.failing_web_step(); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_when_a_web_step_fails_to_find_an_element() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.no_such_element_web_step(); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_with_a_description_and_a_cause_when_a_web_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.failing_web_step(); ArgumentCaptor<StepFailure> argument = ArgumentCaptor.forClass(StepFailure.class); verify(listener).stepFailed(argument.capture()); assertThat(argument.getValue().getDescription().getStepClass().getName(), is(SimpleTestScenarioSteps.class.getName())); assertThat(argument.getValue().getDescription().getName(), is("failing_web_step")); assertThat(argument.getValue().getException().getClass().getName(), is(WebDriverException.class.getName())); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_when_a_method_that_is_not_a_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_with_failing_ordinary_method(); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_with_a_description_and_a_cause_when_a_non_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_with_failing_ordinary_method(); ArgumentCaptor<StepFailure> argument = ArgumentCaptor.forClass(StepFailure.class); verify(listener).stepFailed(argument.capture()); assertThat(argument.getValue().getMessage(), is("java.lang.AssertionError: Oops!")); } @Test public void the_proxy_should_notify_listeners_when_a_web_method_that_is_not_a_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_with_failing_web_method(); verify(listener, times(1)).stepFailed(any(StepFailure.class)); } @Test public void the_proxy_should_notify_listeners_with_a_description_and_a_cause_when_a_web_non_step_fails() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); steps.step_with_failing_web_method(); ArgumentCaptor<StepFailure> argument = ArgumentCaptor.forClass(StepFailure.class); verify(listener).stepFailed(argument.capture()); assertThat(argument.getValue().getMessage(), containsString("Oops!")); } @Test public void the_proxy_notifies_listeners_of_the_test_outome_when_the_test_is_finished() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); StepEventBus.getEventBus().testStarted("SimpleTestScenarioSteps"); steps.step_one(); steps.step2(); steps.step3(); StepEventBus.getEventBus().testFinished(testOutcome); ArgumentCaptor<TestOutcome> argument = ArgumentCaptor.forClass(TestOutcome.class); verify(listener).testFinished(argument.capture()); TestOutcome result = argument.getValue(); assertThat(result, is(testOutcome)); } @Test public void the_proxy_calls_nested_step_methods() { SimpleTestScenarioSteps steps = factory.getStepLibraryFor(SimpleTestScenarioSteps.class); StepEventBus.getEventBus().testStarted("SimpleTestScenarioSteps"); steps.nested_steps(); verify(driver).get("nested_steps"); verify(driver).get("nested.step_one"); verify(driver).get("nested.step_two"); verify(driver).get("nested.step_three"); verify(driver).get("nested.nested.step_one"); } @Test public void the_proxy_skipped_nested_step_methods_after_failure_if_nested_calls_are_disabled() { NestedDatabaseScenarioSteps steps = factory.getStepLibraryFor(NestedDatabaseScenarioSteps.class); DatabaseAPI databaseAPI = Mockito.mock(DatabaseAPI.class); steps.databaseAPI = databaseAPI; StepEventBus.getEventBus().testStarted("SimpleTestScenarioStepsWithNestedCallsDisabled"); steps.steps_with_failure(); verify(databaseAPI, never()).call("nested.method"); } @Test public void the_proxy_skipped_nested_step_methods_after_errors_if_nested_calls_are_disabled() { NestedDatabaseScenarioSteps steps = factory.getStepLibraryFor(NestedDatabaseScenarioSteps.class); DatabaseAPI databaseAPI = Mockito.mock(DatabaseAPI.class); steps.databaseAPI = databaseAPI; StepEventBus.getEventBus().testStarted("SimpleTestScenarioStepsWithNestedCallsDisabled"); steps.steps_with_error(); verify(databaseAPI, never()).call("nested.method"); } @Test public void the_proxy_skipped_nested_step_methods_after_ignored_steps_if_nested_calls_are_disabled() { NestedDatabaseScenarioSteps steps = factory.getStepLibraryFor(NestedDatabaseScenarioSteps.class); DatabaseAPI databaseAPI = Mockito.mock(DatabaseAPI.class); steps.databaseAPI = databaseAPI; StepEventBus.getEventBus().testStarted("SimpleTestScenarioStepsWithNestedCallsDisabled"); steps.steps_with_error(); verify(databaseAPI, never()).call("nested.method"); } @Test public void the_proxy_runs_nested_step_methods_if_no_failure_has_occurred_even_if_nested_calls_are_disabled() { NestedDatabaseScenarioSteps steps = factory.getStepLibraryFor(NestedDatabaseScenarioSteps.class); DatabaseAPI databaseAPI = Mockito.mock(DatabaseAPI.class); steps.databaseAPI = databaseAPI; StepEventBus.getEventBus().testStarted("SimpleTestScenarioStepsWithNestedCallsDisabled"); steps.steps_without_failure(); verify(databaseAPI).call("nested.method"); } static class IsolatedTestScenarioStepsWithPages { private final Pages pages; public IsolatedTestScenarioStepsWithPages(Pages pages) { this.pages = pages; } public WebDriver getDriver() { return pages.getDriver(); } public void step1() { getDriver().get("step_one");} public void step2() { getDriver().get("step_two");} public void step3() { getDriver().get("step_three");} } @Test public void the_proxy_should_execute_steps_for_step_libraries_with_no_parent_class() { IsolatedTestScenarioStepsWithPages steps = factory.getNewStepLibraryFor(IsolatedTestScenarioStepsWithPages.class); steps.step1(); steps.step2(); steps.step3(); verify(driver).get("step_one"); verify(driver).get("step_two"); verify(driver).get("step_three"); } }