JBehave steps

Before we start writing steps, install the PhantomJS browser. The instructions for your operating system can be found at http://phantomjs.org/download.html.

With PhantomJS installed, it is time to specify a few Gradle dependencies:

dependencies { 
    testCompile 'junit:junit:4.+' 
    testCompile 'org.jbehave:jbehave-core:3.+' 
    testCompile 'com.codeborne:selenide:2.+' 
    testCompile 'com.codeborne:phantomjsdriver:1.+' 
} 

You are already familiar with JUnit and JBehave Core, which was set up earlier. Two new additions are Selenide and PhantomJS. Refresh Gradle dependencies so that they are included in your IDEA project.

Now, it is time to add the PhantomJS WebDriver to our Steps class:

public class Steps { 
 
  private WebDriver webDriver; 
 
  @BeforeStory 
  public void beforeStory() { 
    if (webDriver == null) { 
      webDriver = new PhantomJSDriver(); 
      webDriverRunner.setWebDriver(webDriver); 
      webDriver.manage().window().setSize(new Dimension(1024, 768));
    }
  }
} 

We're utilizing the @BeforeStory annotation to define the method that we're using to do some basic setup. If a driver is not already specified, we're setting it up to be PhantomJSDriver. Since this application will look different on smaller devices (phones, tablets, and so on), it is important that we specify clearly what the size of the screen is. In this case, we're setting it to be a reasonable desktop/laptop monitor screen resolution of 1024 x 768.

With setup out of the way, let us code our first pending step. We can simply copy and paste the first method JBehave suggested for us in the report:

@Given("user is on the books screen") 
public void givenUserIsOnTheBooksScreen() { 
// PENDING 
} 

Imagine that our application will have a link that will open the book's screen.
To do that, we'll need to perform two steps:

  1. Open the website home page.
  2. Click on the books link in the menu.

We'll specify that this link will have the ID books. IDs are very important as they allow us to easily locate an element on the page.

The steps we described earlier can be translated to the following code:

private String url = "http://localhost:9001"; 

@Given("user is on the books screen") public void givenUserIsOnTheBooksScreen() { open(url); $("#books").click(); }

We're assuming that our application will run on the 9001 port on the localhost. Therefore, we are first opening the home page URL and then clicking on the element with the ID books. Selenide/JQuery syntax for specifying an ID is #.

If we run our runner again, we'd see that the first step failed and the rest is still in the Pending state. Now, we are in the red state of the Red-Green-Refactor cycle.

Let us continue working on the rest of the steps used in the first scenario. The second one can be the following:

@Then("field bookId exists") 
public void thenFieldBookIdExists() { 
  $("#books").shouldBe(visible); 
} 

The third one is almost the same, so we can refactor the previous method and convert an element ID into a variable:

@Then("field $elementId exists") 
public void thenFieldExists(String elementId) { 
  $("#" + elementId).shouldBe(visible); 
} 

With this change, all the steps in the first scenario are done. If we run our tests again, the result is the following:

The first step failed since we did not even start working on the implementation of our book store application. Selenide has a nice feature that creates a screenshot of the browser every time there is a failure. We can see the path in the report. The rest of the steps are in the not performed state since the execution of the scenario stopped on failure.

What should be done next depends on the structure of the team. If the same person is working both on functional tests and the implementation, he could start working on the implementation and write just enough code to make this scenario pass. In many other situations, separate people are working on the functional specification and the implementation code. In that case, one could continue working on the missing steps for the rest of the scenarios, while the other would start working on the implementation. Since all scenarios are already written in text form, a coder already knows what should be done and the two can work in parallel. We'll take the former route and write the code for the rest of the pending steps.

Let's go through the next scenario:

We already have half of the steps done from the previous scenario, so there are only two pending. After we click on the newBook button, we should set some values to the form, click on the saveBook button, and verify that the book was stored correctly. We can do the last part by checking whether it appeared in the list of available books.

The missing steps can be the following:

@When("user sets values to the book form")
public void whenUserSetsValuesToTheBookForm() {
  $("#bookId").setValue("123");
  $("#bookTitle").setValue("BDD Assistant");
  $("#bookAuthor").setValue("Viktor Farcic");
  $("#bookDescription")
.setValue("Open source BDD stories editor and runner"); }
@Then("book is stored") public void thenBookIsStored() { $("#book123").shouldBe(present); }

The second step assumes that each of the available books will have an ID in the format book[ID].

Let us take a look at the next scenario:

Like in the previous scenario, there are two steps pending development. We need to have a way to select a book and to verify that data in the form is correctly populated:

@When("user selects a book") 
public void whenUserSelectsABook() { 
  $("#book1").click(); 
} 
 
@Then("book form contains all data") 
public void thenBookFormContainsAllData() { 
  $("#bookId").shouldHave(value("1")); 
  $("#bookTitle").shouldHave(value("TDD for Java Developers"));
  $("#bookAuthor").shouldHave(value("Viktor Farcic")); 
  $("#bookDescription").shouldHave(value("Cool book!")); 
} 

These two methods are interesting because they not only specify the expected behavior (when a specific book link is clicked, then a form with its data is displayed), but also expect certain data to be available for testing. When this scenario is run, a book with the ID 1, title TDD for Java Developers, author Viktor Farcic, and description Cool book! should already exist. We can choose to add that data to the database or use a mock server that will serve the predefined values. No matter what the choice of how to set test data is, we can finish with this scenario and jump into the next one:

The implementation of the pending steps could be the following:

@When("user sets new values to the book form")
public void whenUserSetsNewValuesToTheBookForm() {
  $("#bookTitle").setValue("TDD for Java Developers revised");
  $("#bookAuthor").setValue("Viktor Farcic and Alex Garcia");
  $("#bookDescription").setValue("Even better book!"); 
  $("#saveBook").click(); 
} 
 
@Then("book is updated") 
public void thenBookIsUpdated() { 
  $("#book1").shouldHave(text("TDD for Java Developers revised"));
  $("#book1").click();
  $("#bookTitle").shouldHave(value("TDD for Java Developers revised"));
  $("#bookAuthor").shouldHave(value("Viktor Farcic and Alex Garcia")); 
  $("#bookDescription").shouldHave(value("Even better book!")); 
} 

Finally, there is only one scenario left:

We can verify that a book is removed by verifying that it is not in the list of available books:

@Then("book is removed") 
public void thenBookIsRemoved() { 
  $("#book1").shouldNotBe(visible); 
} 

We're finished with the steps code. Now, the person who is developing the application not only has requirements but also has a way to validate each behavior (scenario). He can move through the Red-Green-Refactor cycle one scenario at a time.

The source code can be found in the 02-steps branch of the tdd-java-ch08-books-store Git repository: https://bitbucket.org/vfarcic/tdd-java-ch08-books-store/branch/02-steps.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.110.183