Thursday, 10 May 2018

Automated Acceptance Testing using Cucumber-JVM

This post will give a quick introduction to Cucumber-JVM and an easy-to-follow tutorial to start using Cucumber for writing and automating features.

Quick introduction to Cucumber

Cucumber is a BDD (Behavior Driven Development) framework originally written for Ruby. Cucumber-JVM is the pure Java implementation of Cucumber. Its quite matured now and has almost all the features available in original Cucumber implementation. Cucumber is less a test automation tool and more a collaboration tool between the business and technology people in the team. Cucumber supports describing the behavior of your system in a domain specific natural language called Gherkin. Cucumber features files written in Gherkin syntax act as documentation as well as automated acceptance tests.

How does a Cucumber feature looks like?

Feature: Title of your feature
    In order to <meet some goal>
    As a <type of stakeholder>
    I want <a feature>

Scenario: Title of the first scenario 
    Given <some context or precondition>
    And <additional precondition> optional
    When <some action is performed>
    Then <some result or post condition>
    And <additional post conditions> optional

Scenario: Title of the second scenario
...........
..........

How does it work?

Once the behavior of a system is written in feature files like above by the business/solution analysts, developers/test engineers will write the underlying glue code (or step definitions) to actually test the system. The glue code can be direct calls to application code or calls to other libraries/APIs (like WebDriver, RestAssured, etc) depending on the application and type of test.

In this blog we are going to write and automate acceptance tests using Cucumber and Selenium WebDriver for a simple WikiPedia search functionality.
 

Create and configure your project in Eclipse

Create a new Maven project in Eclipse with below details:
Archetype: maven-archetype-quickstart
Group Id: com.cucumber

Artifact Id: learncucumber

Your project structure in "Package Explorer" will look like below:

Add a new source folder "src/test/resources"  in the project and then add a new folder named "features" under this source folder. Your project will now look like below:


Now edit the pom.xml, add below two dependencies and save the pom.xml:

<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
       <dependency>
         <groupId>org.seleniumhq.selenium</groupId>
         <artifactId>selenium-java</artifactId>
         <version>3.10.0</version>
       </dependency>
       <!-- https://mvnrepository.com/artifact/info.cukes/cucumber-java -->
       <dependency>
         <groupId>info.cukes</groupId>
         <artifactId>cucumber-java</artifactId>
         <version>1.2.5</version>
       </dependency>   

All the required jars for Cucumber and WebDriver will be downloaded. Now add a new folder named "drivers" in "src/test/resources" source folder, download and place "chromedriver.exe" in this folder. Project will look like below:




Now the project is ready to add our first feature file and automate it.

Add a new feature file

Before we add our first Cucumber feature file, we need to install cucumber-eclipse plugin from update site https://cucumber.io/cucumber-eclipse/update-site

Add a new file named "SearchCountry.feature" in the "src/test/resources/features" folder. The cucumber-eclipse plugin will create the SearchCountry.feature file with an example Gherkin feature file as shown below:

Change the content of the file to below text:

#This feature enables a user search for country names
Feature: Search for country names
  In order to know about few countries
  As a user
  I want to search few country names
#First scenario
Scenario: Search for an Asian country
  Given I am on Main Page
  When I search for country "India"
  Then I verify "India" displays in the header

In the eclipse IDE all of the above three steps will appear in a yellow color (with warning sign) indicating the steps do not have matching glue code as shown below.

Right click on the feature file in the editor and "Run As" Cucumber Feature. Once run, the console will print below log:

1 Scenarios (1 undefined)
3 Steps (3 undefined)

You can implement missing steps with the snippets below:

@Given("^I am on Main Page$")
public void i_am_on_Main_Page() throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@When("^I search for country \"([^\"]*)\"$")
public void i_search_for_country(String arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Then("^I verify \"([^\"]*)\" displays in the header$")
public void i_verify_displays_in_the_header(String arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

Write the glue code (step implementation)

Create a new java class named "WikipediaStepDefinitions" in the "com.cucumber.learncucumber" package  of "learncucumber/src/test/java" source folder and  copy and paste the code snippets from console. Save the file. If you run the feature again you will see below result in console:


Feature: Search for country names
  In order to know about few countries
  As a user
  I want to search few country names

  #First scenario
  Scenario: Search for an Asian country          # C:/workspace/cucumber/learncucumber/src/test/resources/features/SearchCountry.feature:7
    Given I am on Main Page                      # WikipediaStepDefinitions.i_am_on_Main_Page()
      cucumber.api.PendingException: TODO: implement me
       at com.cucumber.learncucumber.WikipediaStepDefinitions.i_am_on_Main_Page(WikipediaStepDefinitions.java:17)
       at ?.Given I am on Main Page(C:/workspace/cucumber/learncucumber/src/test/resources/features/SearchCountry.feature:8)

    When I search for country "India"            # WikipediaStepDefinitions.i_search_for_country(String)
    Then I verify "India" displays in the header # WikipediaStepDefinitions.i_verify_displays_in_the_header(String)

1 Scenarios (1 pending)
3 Steps (2 skipped, 1 pending)




This is because the only thing our implementation methods do is to throw an exception indicating that implementation of these methods are still pending. Lets complete the implementation.


We need to launch a browser before executing our scenario - the best way to do that in Cucumber is to use a separate method annotated with @Before which will be executed before each scenario execution. This is similar to using @Before in JUnit and is called a "hook" in Cucumber. We will also use @After hook to quit a browser. Final implementation of "WikipediaStepDefinitions" will be as below:

package com.cucumber.learncucumber;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import cucumber.api.java.After;
import cucumber.api.java.Before;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import junit.framework.Assert;
public class WikipediaStepDefinitions {
      
       WebDriver driver;
      
       @Before
       public void launchBrowser(){
              System.setProperty("webdriver.chrome.driver", "src/test/resources/drivers/chromedriver.exe");
              driver = new ChromeDriver();           
       }
      
       @Given("^I am on Main Page$")
       public void i_am_on_Main_Page() throws Throwable {
           driver.get("https://en.wikipedia.org/wiki/Main_Page");
       }
       @When("^I search for country \"([^\"]*)\"$")
       public void i_search_for_country(String countryName) throws Throwable {
           WebElement searchField = driver.findElement(By.id("searchInput"));
           WebElement searchButton = driver.findElement(By.id("searchButton"));          
           searchField.sendKeys(countryName);
           searchButton.click();    
       }
       @Then("^I verify \"([^\"]*)\" displays in the header$")
       public void i_verify_displays_in_the_header(String countryName) throws Throwable {
           WebElement headerField = driver.findElement(By.id("firstHeading"));
           Assert.assertEquals(countryName, headerField.getText());
       }
      
       @After
       public void quitBrowser(){
              driver.quit();            
       }
}


Run the feature again and it will pass this time.

Make the code better

We have direct calls to WebDriver API in our "WikipediaStepDefinitions" class above. This is certainly not a good way to write your step implementation. Now lets refactor and place our WebDriver calls in PageObject classes. Refactored and improved project code is available in https://github.com/pradip79/learn-cucumber.git

Now add new scenarios to the existing feature file or add new feature file with new scenarios and automate those. Comment if you need additional info..


Wednesday, 2 May 2018

Build and deploy your simple web app using Jenkins, GitHub and Tomcat

In this post we are going to create a simple "Hello World" maven web app project in Eclipse, push the project into GitHub repository, build & deploy to Tomcat using Jenkins. Lets start by creating a sample web application and placing it in GitHub.

Create a maven project and push it into GitHub

(Skip this if you already have your project in GitHub)

Step 1:
File à New à Maven Project àClick Next in the below “New Maven Project” dialog.



Step 2:
Select “maven-archetype-webapp” à Click Next.


Step 3:
Enter Group Id (say "com.demo.webapp"), Artifact Id (say "mywebapp") à Click Finish.



Step 4:
Open "Git Repositories View" through Window à Show View à Other à Git à Git
        Repositories à Click OK



Step 5:
Login to GitHub and create a new repository checking option "Initialize this repository with a README” as shown below.


Step 6:
Copy the repository URL clicking on Clone or download button.


Step 7:
In eclipse “Git Repositories” view click on Clone a Git repository link. In the "Clone Git        Repository" dialog paste the copied URL in the URI field, enter authentication information and click Next.



Step 8:
Keep the default branch selection and click Next


Step 9:
Browse the local storage directory for the cloned repo or keep default selection and click Finish. Cloned repo will be displayed in the "Git Repositories" view.






Step 10:  
Right click on your project name in eclipse à Team à Share Project..  Select the cloned repository from dropdown and click Finish.




The project will now be shared and repo name and branch name will be displayed beside project
name as shown below.



Step 11:
Window à Show View à Other à Git à Git Staging à Click OK to open Git Staging view.        Select the “Working Tree” in “Git Repositories” view. All the project files are displayed under “Unstaged Changes” section in the staging view as shown below.


Step 12:
Right click on the Working Tree and select “Add to Index”. All the project files are now          displayed under “Staged Changes” section in the staging view as shown below.


Step 13:
Enter a commit message and click Commit and Push. Enter GitHub account access details and click OK.


Click OK on the next dialog.

Step 14:
Open GitHub repository and now the project contents will be displayed there. We have our project in GitHub finally...

 


Add a new Maven project in Jenkins and configure it to build and deploy

Step 1:
Through "Manage Jenkins" à "Manage Plugins",  install below plugins if not already installed:

Git, GitHub, Deploy to container Plugin, Maven Integration plugin

Step 2:
In Jenkins add a new item of type Maven project



Step 3:
In the project configuration page check “GitHub project” checkbox and enter the GitHub repository URL as shown below:



In the “Source Code Management” section again enter the same repository URL.

Click Add à Click Jenkins to open “Jenkins Credentials Provider” dialog as below.

Enter GitHub Username and Password, enter a description (say GitHub) and click Add




Select the newly added credentials from the "Credentials" dropdown.




In the "Build Triggers" section check "Poll SCM" and enter H/5 * * * *  in the "Schedule" text area [which will poll GitHub every 5 min]



In the "Build" section enter below information:

Root POM: mywebapp/pom.xml 
Goals and options: clean package

In the "Post-build Actions" section enter a post-build action of type "Deploy war/ear to a container" and enter the below information.

WAR/EAR files: **/*.war
Context path: mywebapp
Container: Tomcat 8.x
Credentials: Add a new Jenkins credentials with the Username and Password of  a user having tomcat "manager-script" role access.
Tomcat URL: http://localhost:8080 [Enter your tomcat URL]





Click Apply and click Save.

Step 4:
Run a new build of the newly created project in Jenkins. If the build is a "Success" open your browser and get to the URL "http://localhost:8080/mywebapp/".  "Hello World!" will be displayed in the browser. We have built and deployed our new web project using Jenkins and Maven.

Step 5:
Now make some changes in our web application source code by adding "Better" to "Hello World!" in the index.jsp, commit and push the changes.

Wait for few minutes [less than 5 min] and a new Jenkins build of our project will be kicked off automatically. Once the new build gets completed refresh your browser and now "Hello Better World!" will be displayed.