Skip to content

BasicTutorial

John Ferguson Smart edited this page Jul 25, 2011 · 1 revision

What is Thucydides

Thucydides is a tool that lets you use WebDriver-based unit or BDD tests to write more flexible and more reusable WebDriver-based tests, and also to generate documentation about your acceptance tests, including a narrative description of test, along with the corresponding screen shots, and also high-level summaries and aggregations of the test results.

Thucydides provides some useful tools built on top of WebDriver to make writing WebDriver tests simpler and more flexible. It also generates reports that document individual test results (including screenshots for each stage of the tests), and also aggregate reports documenting the tests and test cases executed for each user story. It uses Java and JUnit, and fits in well with existing tools.

General introduction

To get an idea of the ideas behind Thucydides, and a general picture of what it does, check out the following presentation:

Completing the circle - Automated web tests as a team communication tool <iframe src="http://www.slideshare.net/slideshow/embed_code/8430564" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>

A simple walkthrough

Let's look at a practical example. Suppose we want to write a simple web test for the Apache web site. Using Thucydides, we would implement this in the three layers we just discussed.

Defining the User Story and the Test Scenarios

The first step would be to write a JUnit class that represents the Use Case or User Story you want to test. A User Story typically has a number of acceptance criteria. In a web/ATDD context, these acceptance criteria are concrete scenarios where a user walks through the application in a particular way. There is typically a "happy-day" scenario, as well as a number of edge cases. Each of these is represented as a JUnit test. Consider the following example:

@RunWith(ThucydidesRunner.class)
public class OpenApacheHomePageWithTitleSample {

    @Managed
    public WebDriver webdriver;

    @ManagedPages(defaultUrl = "http://www.apache.org")
    public Pages pages;

    @Steps
    public ApacheScenarioSteps steps;
    
    @Test
    @Title("The user navigates to the Apache project page.")
    public void the_user_opens_the_page() {
        steps.clickOnProjects();
        steps.clickOnCategories();
    }
}

This is fairly typical of a Thucydides test case. First of all, you need to use the ThucydidesRunner test runner, which injects the Thucydides-specific features and behavior into the test.

The @Managed annotation identifies a WebDriver instance that will be used for your tests. The ThucydidesRunner test runner takes care of instantiating the driver and maintaining the browser open during the tests. It also uses the driver to take screenshots at each stage. Only the Firefox and Chrome WebDriver drivers currently support taking screenshots, so only these two will work currently.

You also need to define a Pages field, annotated by the @ManagedPages annotation. This object handles navigation between pages, and acts as a factory to provide the right Page Object for whatever URL you happen to be on. The defaultUrl attribute indicates the base URL used during the tests if no other URL is provided. The can be overridden using the webdriver.base.url command-line property.

Finally, you need to define a Steps field. This object contains the steps, or actions that you will use to build up your scenarios.

Once this is done, you can write your tests. Let's look at this test in more detail:

@Test
@Title("The user navigates to the Apache project page.")
public void the_user_opens_the_page() {
    steps.clickOnProjects();
    steps.clickOnCategories();
}

You build a test by invoking methods in your Steps and Pages objects (mostly the Steps object). Thucydides automatically opens a browser to the default base URL at the start of the tests. Then, you invoke a number of steps using the Steps object. This will step through the application, taking screenshots at each stage. The steps can contain asserts, and may fail (think of them as mini unit tests). You can also add asserts in the test itself, as shown in this example:

@Test
public void the_user_opens_the_page() {
    steps.clickOnProjects();
    assertThat(webdriver.getTitle(), is("Welcome to The Apache Software Foundation!"));

    steps.clickOnCategories();
    assertThat(webdriver.getTitle(), is("Apache Software Foundation - Projects"));
}    

At the end of the call, JUnit will automatically wrap up and report the final results of the test.

Defining the steps

Steps define actions that a user performs during an acceptance test scenario. They are just normal methods. You can reuse steps in more than one test, pass parameters, and perform asserts.

You define steps in a ScenarioSteps class. The only constraint is that it must have a constructor that accepts a Pages object, as shown here:

public class JobScenarioSteps extends ScenarioSteps {

    public JobScenarioSteps(Pages pages) {
        super(pages);
    }
	...
}

Then you define your steps. A step is identified by the @Step annotation, but can have any method signature:

@Step
public void the_customer_navigates_to_the_initial_page() {
    ...
}

The name is important. Thucydides generates narrative-style reports, so a step should have a clear and meaningful name, written in a narrative style. Thucydides will change camel-case or underscored method names into English sentences in the reports. If you want something more complete to go in the reports, you can use the @Description annotation:

@Step
@Description("Customer navigates to the initial web site page.")
public void the_customer_navigates_to_the_initial_page() {
    ...
}

Inside a step, you can use the Pages object to obtain the right page object for this page. You need to know the Page Object you expect (e.g. HomePage in the following example), but the Pages object will check that the current URL is compatible with this page object, and throw a WrongPageException if you are not in the right place:

@Step
public void the_customer_navigates_to_the_classifieds_page() throws WrongPageException {

    HomePage page = getPages().currentPageAt(HomePage.class);
            
    page.clickOnClassifieds();
    
    assertThat(page.getTitle(), startsWith("Classifieds"));        
}

The following example illustrates a step entering data into fields on a page:

@Step
public void the_customer_enters_job_details() {

    JobDetailsPage page = getPages().currentPageAt(JobDetailsPage.class);

    page.setJobTitle("Widget salesman");
    page.setJobCategory("IT");
    page.setJobSubCategory("Computers & IT");
    page.setJobLocation("ASHTONFIELD 2323, NSW");
    page.setReferenceNumber("123456780");
    page.setFirstName("Joe");
    page.setLastName("Blogs");
    page.showContactName(true);
    page.setEmail("[email protected]");
    page.setPhone("0249123456");
    page.setDescription("A seller of widgets.");

    page.clickOnContinue();
}

You can also group steps into reusable functions using the @StepGroup annotation.

@StepGroup("Group of steps")
public void groupOfStepsContainingAFailure() {
    stepThatSucceeds();
    stepThatFails();
    stepThatShouldBeSkipped();
}

When steps are nested within a step group, they will appear on the reports as nested steps. You can also nest groups within groups.

You can also mark a step as Pending, which is useful if you want the step to appear in the reports, but the require pages or features haven't been implemented yet:

@Step @Pending
public void the_customer_chooses_ad_style_and_start_date() {}       

You can also temporarily ignore a step using @Ignore

@Step @Ignore
public void dont_run_this_test_just_yet() {}  

Learning more

And more documentation will be coming soon!

ATDD - Test-first or test-last?

When writing your web tests, you can either adopt a test-first approach or a test-last approach. Which you choose depends on the nature of your project (is this a new application, or are you writing regression tests for an existing application), of the requirements (do you have a set of existing manual test scripts that you want to automate), and of personal preference.

If you adopt a test-first approach, you would start off by writing the test scenarios, discover what steps you need to implement, and in turn discover what your Page Objects should look like. You can flag the test scenarios as @Pending until they are ready to execute. I find this a very efficient way to implement clean and effective tests.

Where does the name come from?

Thucydides was the world's first eye-witness historian - he was the first (and one of the only) historian to record what he saw, as it happened:

Thucydides was a Greek historian from Alimos and author of the History of the Peloponnesian War, which recounts the 5th century BC war between Sparta and Athens to the year 411 BC. Thucydides has been dubbed the father of "scientific history" because of his strict standards of evidence-gathering and analysis in terms of cause and effect without reference to intervention by the gods, as outlined in his introduction to his work." (Wikipedia)