Thucydides integrates smoothly with ordinary JUnit 4 tests, using the ThucydidesRunner test runner and a few other specialized annotations. This is one of the easiest ways to start out with Thucydides, and is very well suited for regression testing, where communication and clarification with the various stakeholders is less of a requirement.
Here is an example of a Thucydides JUnit web test:
@RunWith(ThucydidesRunner.class) @Story(UserLookForJobs.class) public class LookForJobsStory { @Managed public WebDriver webdriver; @ManagedPages(defaultUrl = "http://localhost:9000") public Pages pages; @Steps public JobSeekerSteps job_seeker; @Test public void user_looks_for_jobs_by_key_word() { job_seeker.opens_jobs_page(); job_seeker.searches_for_jobs_using("Java"); job_seeker.should_see_message("No jobs found."); } @Test public void when_no_matching_job_found_should_display_error_message() { job_seeker.opens_jobs_page(); job_seeker.searches_for_jobs_using("unknownJobCriteria"); job_seeker.should_see_message("No jobs found."); } @Pending @Test public void tags_should_be_displayed_to_help_the_user_find_jobs() {} @Pending @Test public void the_user_can_list_all_of_the_jobs_for_a_given_tag() {} @Pending @Test public void the_user_can_see_the_total_number_of_jobs_on_offer() {} }
Let’s examine this section-by-section. The class starts with the @RunWith annotation, to indicate that this is a Thucydides test. We also use the @Story annotation to indicate which user story (defined as nested classes of the the @Feature classes above) is being tested. This is used to generate the aggregate reports.
@RunWith(ThucydidesRunner.class) @Story(UserLookForJobs.class) public class LookForJobsStory { ...
Next, come two essential annotations for any web tests. First of all, your test case needs a public Webdriver
field, annotated with the @Managed
annotation. This enables Thucydides to take care of opening and closing a WebDriver driver for you, and lets Thucydides use this driver in the pages and test steps when the tests are executed:
@Managed public WebDriver webdriver;
The second essential field is an instance of the Pages
class, annotated with the @ManagedPages
annotation. This is essentially a page factory, that Thucydides uses to provide you with instantiated page objects. The defaultUrl
attribute lets you define a URL to use when your pages open, if no other base URL has been defined. This is useful for IDE testing:
@ManagedPages(defaultUrl = "http://localhost:9000") public Pages pages;
Note that these two annotations are only required for web tests. If your Thucydides test does not use web tests, you can safely leave them out.
For high-level acceptance or regression tests, it is a good habit to define the high-level test as a sequence of high-level steps. It will make your tests more readable and easier to maintain if you delegate the implementation details of your test (the "how") to reusable "step" methods. We will discuss how to define these step methods later. However, the minimum you need to do is to define the class where the steps will be defined, using the @Steps
annotation. This annotation tells Thucydides to listen to method calls on this object, and (for web tests) to inject the WebDriver instance and the page factory into the Steps class so that they can be used in the step methods.
@Steps public JobSeekerSteps job_seeker;
Tests that contain no steps are considered to be pending. Alternatively, you can force a step to be skipped (and marked as pending) by using the @Pending
annotation or the @Ignore
annotation. Note that the semantics are slightly different: @Ignore
indicates that you are temporarily suspending execution of a test, whereas @Pending
means that the test has been specified but not yet implemented. So both these tests will be pending:
@Test public void administrator_adds_an_existing_company_to_the_system() {} @Pending @Test public void administrator_adds_a_company_with_an_existing_code_to_the_system() { steps.login_to_admin_page(); steps.open_companies_list(); steps.select_add_company(); // More to come }
A test is also considered pending if any of the steps used in that test are pending. For a step to be pending, it needs to be annotated with the @Pending
annotation.
You can use junit assumptions in your tests or step methods to . Steps where the conditions under assumptions fail are marked as PENDING instead of ERROR. Subsequent steps are also marked as PENDING.
@Test public void administrator_adds_an_existing_company_to_the_system() {} @Test public void administrator_adds_a_company_with_an_existing_code_to_the_system() { steps.login_to_admin_page(); steps.open_companies_list(); steps.select_add_company(); // More to come }
... @Step public void open_companies_list() { Assume.assumeThat(user.role, is("admin")); CompaniesListPage page = pages().get(CompanyListPage.class); String companieslist = page.getCompaniesList(); } ...
In the above example, if the assumption in step open_companies_list
fails, it and all susbequent steps will be marked PENDING.
Normally, Thucydides opens a new browser session for each test. This helps ensure that each test is isolated and independent. However, sometimes it is useful to be able to run tests in a single browser session, in particular for performance reasons on read-only screens. You can do this by using the uniqueSession attribute in the @Managed annotation, as shown below. In this case, the browser will open at the start of the test case, and not close until all of the tests have been executed.
@RunWith(ThucydidesRunner.class) public class OpenStaticDemoPageSample { @Managed(uniqueSession=true) public WebDriver webdriver; @ManagedPages(defaultUrl = "classpath:static-site/index.html") public Pages pages; @Steps public DemoSiteSteps steps; @Test @Title("The user opens the index page") public void the_user_opens_the_page() { steps.should_display("A visible title"); } @Test @Title("The user selects a value") public void the_user_selects_a_value() { steps.enter_values("Label 2", true); steps.should_have_selected_value("2"); } @Test @Title("The user enters different values.") public void the_user_opens_another_page() { steps.enter_values("Label 3", true); steps.do_something(); steps.should_have_selected_value("3"); } }
If you do not need WebDriver support in your test, you can skip the @Managed
and @Pages
annotations, e.g.
@RunWith(ThucydidesRunner.class) @Story(Application.Backend.ProcessSales.class) public class WorkWithBackendTest { @Steps public BackendSteps backend; @Test public void when_processing_a_sale_transation() { backend.accepts_a_sale_transaction(); backend.should_the_update_mainframe(); } }