Part 2: Working with Git…

In this post, we will focus mainly on branching and merging techniques in Git. They are very powerful features in Git. For example, there is a master branch that we are working on it and suddenly we get an idea of a new feature that we want to add but we are not sure if the new feature will work out or not. Now instead of making changes and commits to our master branch and then trying to redo if there is an issue, Git allows us to create a new branch which can be used to add our new feature. We can switch back and forth between master and newly created branch. If the idea doesn’t work out, we can throw away the newly added branch without affecting the master branch and if the idea does work out then Git allows to add changes to the master branch through a process called merging.

Here also we will use our Hybrid Framework project to learn the branching and merging techniques.

Create a new branch

    1. Open the Terminal and navigate to Project folder and then do a git status. Our Master branch will be clean and there will be nothing to commit as displayed below.

      git status
    2. Type in git branch to view all the available branches. Currently, we have only one branch and that is the master branch. Notice the * placed near to Master. This means the head is pointing currently to Master branch.

      git branch
    3. To create a new branch, type in git branch newframework and hit return. To make sure that new branch named ‘newframework’ is created or not, use git branch command to view all branches. Notice that the * is still placed near to Master. This means the head is still pointing to Master branch as the Master code is checked out.

      New Branch
    4. ls -la .git/refs/heads will list out all the heads available. cat .git/refs/heads/newframework will display the hash value of our last commit in ‘newframework’ branch. Notice that this is exactly same as the last commit of our Master branch. Only when we start doing new commits to the new branch the values starts changing.

      Heads
    5. As mentioned in the previous step, currently Master branch is checked out. To checkout ‘newframework’ branch, type in git checkout newframework. We can confirm which branch is checked out by typing in git branch. Using cat .git/HEAD command, we can confirm to which branch HEAD is pointing to. Notice that * is now placed near to ‘newframework’ branch.

      Branch checkout
    6. Now let’s do some changes in our Hybrid Framework project and commit the changes to the new branch ‘newframework’. We will edit the readme file and add few more details as displayed below and then save it.

      Changes done to readme file
    7. To commit the changes in ‘newframework’ branch, first, do git status to confirm on the changes and then do a force commit using the command git commit -am "Updated read me file in newframework branch". Notice that the updated readme.txt file is committed in ‘newframework’ branch and not to master branch.

      Committing to new branch
    8. Using command git log --oneline we can get the log of commits done in ‘newframework’ branch. Notice that the HEAD is pointing to ‘newframework’ branch and also new hash value is created for the new commit.

      git log of newframework branch
    9. To verify that the new changes are not committed to Master branch, use git checkout master command to checkout the Master branch. Use git branch to confirm the checkout.

      Checkout master branch
    10. Once Master branch is checked out, we can open the readme file in our Hybrid Framework project to verify if we have the updated readme file or not. Notice that we no longer have the updated readme file as the current branch is Master.

      readme file from Master branch
    11. To switch back to the new ‘newframework’ branch, type in git checkout newframework. This will checkout the ‘newframework’ branch and we can see the updated readme in eclipse.

      Switched to newframework branch
    12. One point to remember while switching branches is that we need to commit all changes made to existing files in the current branch before switching to another branch. Otherwise, Git will abort the process as there is data loss. However, if are adding new files in the current branch and then try switching to a different branch, Git will allow us to switch as there is no data loss happening.
    13. There are some additional commands that can be used while creating branches.
      // Create a new branch and checkout at same time
      git checkout -b new_branch_name
      
      // Get more detailed log information when using multiple branches
      git log --graph --oneline --decorate --all
      
      // Compare two different branches
      git diff master..newframework
      
      // Find out whether one branch completely contains another branch or not
      git branch --merged
      
      // Renaming a branch
      git branch -m newframework updatedframework
      
      // Delete a branch. Notice that Git will not delete the current branch
      git branch -d updatedframework
      
      // Forcefully Delete a branch. This is used when to be deleted branch has recent commits
      git branch -D updatedframework
      

Merge branches

  1. Now we decide that newly added functionality for which we created a new branch is working perfectly and we want to bring the changes to original branch so that they are the part of main project. Well, in order to do that we will use merging technique. In our case whatever changes that we did in ‘newframework’ branch will be merged to Master branch.
  2. We can view the differences by using git diff master..newframework command. Notice the difference between two readme files found in master and ‘newframework’ branch.

    git diff between master and newframework branch
  3. The first step is that we want to make sure that we checkout the branch that things are being merged into. In our case, the master branch is going to the receive the changes from ‘newframework’ branch. In other words, we will checkout the master branch. Use git checkout master command to checkout the master branch.
  4. To merge use git merge newframework. Here ‘newframework’ is target folder and master is the receiving folder. Once merging is complete, we can verify the same in Eclipse. The readme.txt in master branch is exactly same as ‘newframework’ branch. Notice that the latest SHA value in master branch matches to that in ‘newframework’ branch.

    git merge newframework
  5. The above merging process is called Fast-Forward merge. This is because when we merged ‘newframework’ branch to master branch, there were no changes made to master branch after the creation of ‘newframework’ branch. As a result no new commits are made in master branch, instead, Git just fast-forward the last commit from ‘newframework’ branch to master branch and move HEAD along with it. Now of we do git log --oneline in master branch, we can see that the Hash or SHA value is same as that in ‘newframework’ branch as displayed in above image.
  6. If we don’t want Fast-Forward merge in the above case, we can force Git to make a no Fast-Forward merge using the command git merge --no-ff newframework. This process will create a new commit in the master branch instead of doing the Fast-Forward merge. Similarly, we can also force Git to always do Fast-Forward merge only using the command git merge --ff-only newframework . Notice that this command will work only we can do a  Fast-Forward, otherwise, it will abort.
  7. If there is a commit on the master branch then its no longer a Fast-Forward merge as the HEAD moved to a new commit that’s not in the other branch. To simulate this merge, we will checkout a new branch ‘recursivebranch’ and add a new commit. Similarly, we add a new commit to master also. Although there is a common ancestor in both branches, the HEAD is pointing to a new commit in master which is not there in ‘recursivebranch’.
    New commit in master branch
  8. To merge ‘recursivebranch’ into master master, we will use git merge recursivebranch command. As mentioned earlier, we need to be in the master branch. As soon as we hit enter, Git popped up a Nono editor asking us to enter a commit message. Nano editor gets opened because in .gitconfig file, default editor is set as Nano editor. We will just save with the default message ‘Merge branch ‘recursivebranch”. All of the other lines are going to be ignored and left out of the commit message.

    Commit message in Nano editor
  9. As soon as we Save and Close the Nano editor, below message, will be displayed.

    Recursive strategy
  10. Git has different merging strategies and Recursive is one of them. Now if we do a git log --oneline in both branches, we can see a new commit is done into master branch. It may seem that there is a conflict happening as there are different commits to two different branches but Git is able to successfully handle the merge as it understands that the commit made to master is the latest one. Notice that the SHA value from ‘recursivebranch’ is saved before the last commit in master branch as Git is pretty good at figuring out on how to merge things in.

    Git log
  11. Sometimes when there are two changes to same line or set of lines in two different commits, Git doesn’t know which one to use or how to merge them together and we get a merge conflict.
  12. To create a merge conflict scenario, let create and a new branch ‘mergebranch’ using git checkout -b mergebranch. In this branch, we will update the readme.txt file by deleting some comments and then updating few comments as shown below. Commit the changes to the new branch and then checkout master branch.

    Updated comments in readme.txt file for ‘mergebranch’ branch
  13. In master branch, we will not remove any comments instead just a small update to the existing one as displayed below. Commit the changes into the master branch.

    readme.txt file update in master
  14. Now when we try to merge the ‘mergebranch’ into master branch, we get below merge conflict message from Git. This is because Git is not sure which of the lines it need to prefer from readme.txt file. This is where Git requires our intervention.

    Merge conflict
  15. Now if we open our readme.txt file in eclipse, Git will mark the areas where ever it is finding conflicts. Notice that it will mention which all content belong to HEAD, that is the master branch and which all belongs to ‘mergebranch’ with a bunch of equal signs between them. Now its up to us to go through and decide which block of lines that we require to be committed to master branch.

    Merge conflict displayed in readme.txt file
  16. When we do a git status, we will notice that the merge is not successful and Git wants us to resolve the conflicts in order to complete the merge. We have three choices to resolve merge conflicts and they are to abort, to resolve manually, or to use merge tools. Here we will not take the third approach.
  17. To abort a merge, type in git merge --abort command to abort the merge. This will simply abort the merging process and everything will go back to the state before the merge process.
  18. Another way is to go to readme.txt and manually do the changes. Here we will confirm which block of lines to use and remove the references to HEAD, ‘mergebranch’ and equal signs. Notice that Git will mark the document where ever it is finding conflicts, so the best approach is to scan the entire document which has a conflict.

    Manually resolving merge conflict in readme.txt file
  19. Once we are satisfied with changes, we need to save and close it. We can do git add readme.txt to add the file into staging. And we are ready to commit, we can directly use git commit without any message. Git has a default message for commit during the merge process. We can remove conflict comment and save the default comment.

    git commit for merge
  20. To confirm if the merge is successful or not, we can do git status and then compare logs from both branches as displayed below. Notice that master branch has the last commit from ‘mergebranch’.

    git log –oneline -3 to display first three commits

 

Additional Information:

This is one command in Git using which we can get a graphical representation of all branching and merge that we did. Use the command git log --graph --oneline --all --decorate.

Graphical representation of all branching and merging

With this, we came to an end on few important features of Git. Keep following the blog and hope you were able to follow most of it. Until next post, Happy Learning!

Part 1: Working with Git…

Our next task is to upload our Hybrid Framework project into a local repository using Git and then publish our Git repository in a web-based Git called GitHub.

The question that normally comes when working with Git and GitHub – what is the difference between them?

Git is a version control system that stores data or code more like a set of snapshots of a miniature filesystem. Every time we commit or save the state of our project in Git, it basically takes a picture of what all our files look like at that moment and stores a reference to that snapshot. When we commit changes, it stores locally and if we push the commit, it also stores them remotely (if remote Git repository is set up). When setting remotely, Git act as Distributed Version Control System.

GitHub is a web-based Git. Its mostly used for code and offers all of the Distributed Version Control and Source Code Management functionalities of Git along with some added features. We will discuss more on it in later posts.

In this post, we will do a source version control of our Hybrid Framework project using Git and then learn some branching and merging techniques.

Download and Install Git

    1. Our first step is to download and install Git on our machine. To download Git, click git-scm downloads link to go to downloads page directly. Download the required files based on Mac/Windows/Linux machine. Mac installation is pretty easy and we just need to follow the prompts. We will use Terminal program in Mac for writing Git commands. For Windows, make sure that default options are selected. Git Bash is used in Windows for writing the Git commands.
    2. To check if Git is successfully installed on our machine, open Terminal/Command prompt and type in below commands.
      which git
      git --version
      

      Mac Terminal
    3. Next step is to do some configurations. Git can be configured at System level, User level, and Project level. Use below commands to do configurations at different levels.
      // System Level
      git config --system
      
      // User Level
      git config --global
      
      //Project Level
      git config
      
    4. Here in our case, we will be setting some user-level configurations like setting up the user name, email etc..
      // Configuring name, email
      git config --global user.name "Your name"
      git config --global user.email "Your email"
      
      // Some additional configurations
      // Configuring core editor in Mac
      git config --global core.editor "vim -wl1"
      
      // Configuring core editor in Windows
      git config --global core.editor "notepad.exe -wl1"
      
      // Turning on the different color options in the editor
      git config --global color.ui true
      
      // To list out all configurations
      git config --list
      
      // To view the configurations in .gitconfig file present in user directory
      cat .gitconfig
      
      Git configurations

      .gitconfig file configured at user level
    5. Windows already has auto completion feature but in Mac, we have to install the auto-completion feature. This is optional but it will help if we are working with Mac. There are three steps involved in adding this feature – first, download the file from GitHub, rename the filename by adding “.” and then add it to “.bash_profile” file by adding few lines of code. Once auto-completion is installed, we can type in Git and type any character like ‘h’ and hit tab to auto-complete the command. Here it will open up Git help.
      // Get the auto completion file from GitHub
      curl -OL https://github.com/git/git/raw/master/contrib/completion/git-completion.bash
      
      // Rename the git-completion.bash file to .git-completion.bash file
      mv ~/git-completion.bash ~/.git-completion.bash
      
      // Edit the .bash_profile file with below code
      vim .bash_profile
      
      // Type "i" to insert, to save hit Escape, type in: “:wq + Enter” the vim editor
      // Type in below code
      if
      [ -f ~/.git-completion.bash ]; then
      source ~/.git-completion.bash
      fi
      
      
    6. Once Git is installed and configured, out next task is to initialize our project. For initializing we will use Git init command inside the project folder. Here we will be initializing our Hybrid Framework project.
      // First step is to go project folder
      cd documents/workspace/HybridFramework
      
      // Initialize Hybrid Framework project by using below command
      git init
      

      After successful initialization we will see as below and also a separate .git folder will be created inside our project folder. This .git folder will have all of our tracking information of our project. The .git folder contains many files and folders like HEAD, Config, description, refs etc..

      Git init
    7. Now that our project is initialized, our next task is to do a first commit.

      Project folder with newly added readme text file
    8. To commit, first, we need to add all changes done in the present working directory by using the command git add .. Note that ‘.’ in the command refer to the current working directory. This is followed by command git commit -m "first commit". Here -m refers to the message that we want to share on the commit.

      All our files successfully committed
    9. To view the details of all commits, use the command git log.

      Git log command to view details of all commits
    10. To view the current status of our project, use the command git status. This shows the current status of our working directory, staging index and repository. Note Git follows a three-tier architecture – Working Directory, Staging Index, and Repository. In below screenshot note that the files in red are in the working directory and not moved to the staging area. The same files are added to staging index (files in green color) after executing the command git add. After commit, the files are moved to Repository.

      git status
    11. There are few more commonly used commands in git.
      // To view Head is pointing to which branch
      // If HEAD is pointing to last commit of master branch 
      cat .git/HEAD  --> Shows ref: refs/heads/master
      
      // To view the hash value of last commit in master branch
      // Below command will display the hash value of the last commit in master branch
      cat .git/refs/heads/master --> Shows a hash value like '72eecc8707d97c874dd06aca5ad5198563fefcff'
      
      // For Add/Edit files 
      git add
      git commit -m "Added/Edited files"
      
      // For finding changes between files or compare two files
      // This command compares the files in repository that head is pointing at to those present in working directory 
      git diff
      
      // Compare two files between staging index and those present in repository
      git diff --staged
      
      // Delete files from repository
      // This will remove the file from the repository as well as from working directory.
      git rm filenames
      git commit -m "Deleted files"
      
      // Renaming files
      // This will rename the file from the repository as well as from working directory.
      git mv oldnamefile newfilename
      git commit -m "Renaming files"
      
      // Adding and committing to repository in one step
      git commit -am "adding committing files in one step"
      
      // Undo changes in current working directory. Checkouts the file from repository to working directory
      // -- means stay on current branch
      git checkout -- filename
      
      // Undo changes from staging to working directory or to unstage
      git reset HEAD filename
      
      // Undo changes to last commit in repository. First add it staging and then amend to last commit
      git add filename
      git commit --amend -m "adding to last commit"
      
      // Checkout from a particular commit or retrieving old version
      // Checks out the file into staging
      git checkout shavalue -- filename
      
      // Reset to a particular version can be done by resetting the head
      // --soft Does not touch the staging index file or the working directory at all but resets the head in repository
      git reset --soft shavalue
      
      // --mixed Resets the staging index and repository but not the working directory 
      git reset --mixed shavalue
      
      // --hard Resets the staging index, working directory and repository
      git reset --hard shavalue
      
      // To permanently remove untracked files from working directory
      git clean -f
      git clean -n
      
      // To permanently remove a folder from repository
      git rm -r --cached FolderName
      git commit -m "Removed folder from repository"
      
    12. Till now we have seen Git identifies any changes to our project. However, sometimes we don’t want Git to identify some files like the log files or compiled source codes to be included in our repository. For such cases, we will create a .gitignore file. This file will contain the list of files that need to be ignored. In Windows, we can directly create a text file and name it as .gitignore and save as all files in the project directory. For Mac, we will use a text editor from Terminal to create a .gitignore file inside our project folder.
    13. To create a .gitignore file in Mac, follow below instructions:
      1. Open Terminal and navigate to Project folder.
      2. Type in nano .gitignore
      3. Nono editor opens up in the Terminal
      4. Here we are creating .gitignore file specific to HybridFramework project. We will be ignoring 'Target', 'RESULT_LOG', 'Screenshots', 'test-output' folders as displayed in below screenshot.
      5. Hit Cntrl + X, Y, Return to save the .gitignore file inside project folder.
      

      .gitignore file opened in nano editor
    14. One point to remember that the rules in our .gitignore file only apply to untracked files. Since ‘target’ folder and ‘system.properties’ files are already committed in our repository, first, we have to unstage them and then create a commit in order for the .gitignore file to ignore the directories from the staging index.
      // Remove files from staging index
      git rm -r --cached target
      git rm -r --cached src/main/resources/system.properties
      git commit -m "Removed target and system.properties file"
      
    15. Once .gitignore file saved and target’ folder and ‘system.properties’ files unstaged, we will see only .gitignore file as an untracked file. Remember to store the  .gitignore file in repository.
      .gitignore in working directory
      // To add .gitignore into repository
      git add .gitignore
      git commit -m "Adding .gitignore into repository"
      

In the next post, we will concentrate more on the branching and merging techniques using Git and we will view how its reflected in Eclipse. Till then Happy Learning!

 

Part 5: Hybrid Framework Development…

This is the final post on Hybrid Framework Development. Here we will focus on different reports and logs that will be created after running our test.

  • Before running our test, our ‘HybridFramework’ project structure will be displayed as below.

    HybridFramework Project Structure
  • We have 3 source folders, JRE System Library and Maven Dependencies, src and target folder, pom.xml file in our project.
  • Here we will execute using Chrome browser. For that we just need to update the property <selenium.browser>CHROME</selenium.browser> inside our pom.xml file.
  • After updating the pom.xml file, we have to ensure that the properties are updated in system.properties file inside ‘src/main/resources‘ source folder. Right click on ‘HybridFramework’ project and select Maven -> Update Project as displayed below.

    Update Project
  • After successful update, we can verify the same in system.properties file.

    system.properties file
  • Next task is to execute our test. Right click on ‘HybridFramework’ project and click Run As -> Maven Test.
  • The test gets successfully completed and we can see that two tests are executed. The Logs are printed in console as well as in a log file.

    System Console
  • Refresh the project once we can see three more folders are created – RESULT_LOG, Screenshots, and test-output.

    Output folders
  • The RESULT_LOG folder contains the Extent Report. As displayed below this HTML report has all the actions captured. Also as we coded in the action methods, screenshots are attached too. This report not only looks good but also share vital information of our test.

    Extent Report
  • The Screenshots folder stores all captured screenshots inside the project folder. This framework also ensures that the folders and reports are created based on execution date.

    Reported created based on execution date
  • The test-output folder contains not only the regular TestNG reports but also the log file of our execution. If we execute as Maven Test, the test-output folder will contain only the log file. Notice that we configured the log folder name and the log file name inside Log4j.xml.

    Log file creation
  • The log file once opened will be displayed as below. Notice that it will capture all the log information as we coded inside Page Objects.

    Sample of log file

 

With this, we completed our Hybrid Framework Development. This framework can be successfully implemented for web-based projects that are planning to move from manual QA to Automation QA. This framework takes a modular approach and is very easy to maintain. Apart from that it also creates wonderful reports that can be shared across different teams.

In coming posts we will cover on how to upload our project into a repository and then execute using CI tool. Sounds interesting, right?

Well, for that you need to keep following. Till then, Happy Learning!

 

Part 4: Hybrid Framework Development…

This post is the continuation of Hybrid Framework Development. For a complete understanding of Hybrid Framework Development, please go through the previous posts on Hybrid Framework Development.

Part 1: Hybrid Framework Development…
Part 2: Hybrid Framework Development…
Part 3: Hybrid Framework Development…

In this post, we will be creating a TestNG script. We will create two tests, although the flow is same we will use a different set of test data.

First Test Case: Navigate to New Tours application and perform a successful login. Search for a one-way flight departing from London on 7th.

Second Test Case: Navigate to New Tours application and perform a successful login. Search for a one-way flight departing from Paris on 10th.

  • Right click on the ‘src/test/java‘ folder and click New -> Package. Name the package as ‘com.testmadness.testcase’ and click Finish to close the window.
  • Inside ‘com.testmadness.testcase’ package, we will place our TestNG class. Right click on ‘com.testmadness.testcase’ package and select TestNG -> Create TestNG class. Name the class as ‘BookFlightTest.java’. Copy the below code into ‘BasePage.java’ file.
    package com.testmadness.testcase;
    
    import org.testng.annotations.Test;
    import com.testmadness.pages.HomePage;
    import com.testmadness.utils.Config;
    import com.testmadness.utils.Constants;
    import com.testmadness.utils.ExcelUtil;
    import com.testmadness.utils.Log;
    import com.testmadness.utils.Reports;
    import org.testng.annotations.BeforeMethod;
    import java.lang.reflect.Method;
    import org.apache.log4j.xml.DOMConfigurator;
    import org.openqa.selenium.WebDriver;
    import org.testng.annotations.AfterMethod;
    import org.testng.annotations.BeforeClass;
    
    public class BookFlightTest {
    	WebDriver driver;
    	HomePage homePage;
    	Reports report;
    	
      // First Test Case	
      @Test
      public void firstTestCase() {
    	  homePage = new HomePage(driver);
    	  try {
    		homePage.navigateToURL(ExcelUtil.getCellData(1, 1));
    		homePage.loginUser(ExcelUtil.getCellData(1, 2), ExcelUtil.getCellData(1, 3)).findFlights(ExcelUtil.getCellData(1, 4), ExcelUtil.getCellData(1, 5));
    	} catch (Exception e) {
    		e.printStackTrace();
    	} 
      }
      
      //Second Test Case	 
      @Test
      public void secondTestCase() {
    	  homePage = new HomePage(driver);
    	  try{
    		  homePage.navigateToURL(ExcelUtil.getCellData(2, 1));
    		  homePage.loginUser(ExcelUtil.getCellData(2, 2), ExcelUtil.getCellData(2, 3)).findFlights(ExcelUtil.getCellData(2, 4), ExcelUtil.getCellData(2, 5));
    	  }catch (Exception e) {
    			e.printStackTrace();
    		}  
      }
      
      // Before Method to start logging
      @BeforeMethod
      public void beforeMethod(Method method) {
    	  
              // Passing the test case method name
    	  Log.startTestCase(method.getName());
    	  report.startTest(method.getName());
    	  System.out.println(Config.getProp().getProperty("selenium.browser")); 
      }
      
      // After Method to end logging
      @AfterMethod
      public void afterMethod() {
    	    homePage.endTest();
    	    homePage.endReport();
    	    Log.endTestCase("firstTestCase");
    	  
      }
      
      //Before Class to initialize Log4j
      @BeforeClass
      public void beforeClass() {
    	    System.setProperty(Config.getProp().getProperty("logfoldername"), Constants.logFolderName);
    	    System.setProperty(Config.getProp().getProperty("logfilename"), Constants.logFileName);
    	    DOMConfigurator.configure("src/main/java/com/testmadness/utils/log4j.xml");
    	    try {
    		   ExcelUtil.setExcelFile(Constants.PATH_DATA, "Sheet1");
    		} catch (Exception e) {
    		   e.printStackTrace();
    		}
    		 report = new Reports();
    	}	
    }
    
  • In the above code we have used two ‘@Test’ methods for our two test cases. We have set the path of our test data sheet and completed the Log4j configuration in ‘@BeforeClass’ method. In ‘@BeforeMethod’ we started the Extent Report and Log4j logging. In ‘@AfterMethod’, we stopped the logging as well as closed the test.
  • After successful completion till here, our project will look like as displayed below.

    ‘src/test/java’ folder
  • We can create as many as test scripts using TestNG class. More details are on Part 1: Scripting using TestNG….

With this we completed the scripting part inside our Hybrid Framework. We automated two test cases. In the next post we will go through the reporting and logging which is going to be an interesting topic. Till then, Happy Learning!

 

Part 3: Hybrid Framework Development…

This post is a continuation of Part 1: Hybrid Framework Development… and Part 2: Hybrid Framework Development…

Here we will mainly focus on creating Page Objects of our Demo website as well as use the Page Factory concepts.

For better understanding, please go through the following posts:

Part 1: Page Object Model…
Part 2: Page Object Model…
Working with Page Factory…

  • Our first task is to create a Base class. This Base class will have common methods that will be used in all our Page Object classes. Right click on the ‘src/main/java‘ folder and click New -> Package. Name the package as ‘com.testmadness.base’ and click Finish to close the window.
  • Inside ‘com.testmadness.base’ package, we will place our Base class. Right click on ‘com.testmadness.utils’ package and select New -> Class. Name the class as ‘BasePage.java’. Copy the below code into ‘BasePage.java’ file.
    package com.testmadness.base;
    
    import java.io.File;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    import org.apache.commons.io.FileUtils;
    import org.openqa.selenium.NoSuchElementException;
    import org.openqa.selenium.OutputType;
    import org.openqa.selenium.TakesScreenshot;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.chrome.ChromeDriver;
    import org.openqa.selenium.chrome.ChromeOptions;
    import org.openqa.selenium.firefox.FirefoxDriver;
    import org.openqa.selenium.safari.SafariDriver;
    import org.openqa.selenium.support.PageFactory;
    import org.openqa.selenium.support.ui.ExpectedConditions;
    import org.openqa.selenium.support.ui.Select;
    import org.openqa.selenium.support.ui.WebDriverWait;
    import com.relevantcodes.extentreports.LogStatus;
    import com.testmadness.utils.Config;
    import com.testmadness.utils.Constants;
    import com.testmadness.utils.Log;
    import com.testmadness.utils.Reports;
    
    public class BasePage {
    	
    	public WebDriver driver;
    	private static final int pageElementLoadWait = 30;
    	private static final int pageTimeOutWait = 20;
    	public Reports report = new Reports();
    	public static HashMap<WebElement, String> elementDetails = new HashMap<WebElement, String>();
    	
    	public BasePage(WebDriver driver){
    	    this.driver = driver;
    		if(driver==null){
    			driver = getDriver();	
    		}
    		//Initialize Page Factory. Passing child object
    		PageFactory.initElements(driver, this);
    		Log.info("Page Factory initialized");
    		
    	}
    	
            // Driver Initialization
    	public WebDriver getDriver(){
    		
    		String browser = Config.getProp().getProperty("selenium.browser");
    		
    		if(browser.equals("SAFARI")){
    			System.setProperty("webdriver.safari.driver", Config.getProp().getProperty("webdriver.safari.driver"));
    			driver = new SafariDriver();
    			driver.manage().window().maximize();
    		}
    		else if(browser.equals("FIREFOX")){
    			System.setProperty("webdriver.gecko.driver", Config.getProp().getProperty("webdriver.firefox.driver"));
    			driver = new FirefoxDriver();
    			driver.manage().window().maximize();
    			driver.manage().timeouts().implicitlyWait(pageTimeOutWait, TimeUnit.SECONDS);
    		}
    		else if(browser.equals("CHROME")){
    			System.setProperty("webdriver.chrome.driver", Config.getProp().getProperty("webdriver.chrome.driver"));
    			ChromeOptions options = new ChromeOptions();
    			options.addArguments("--start-maximized");
    			options.addArguments("--disable-web-security");
    			options.addArguments("--no-proxy-server");
    			Map<String, Object> prefs = new HashMap<String, Object>();
    			prefs.put("credentials_enable_service", false);
    			prefs.put("profile.password_manager_enabled", false);
    			options.setExperimentalOption("prefs", prefs);
    			driver = new ChromeDriver(options);
    			driver.manage().timeouts().implicitlyWait(pageTimeOutWait, TimeUnit.SECONDS);
    		}
    		
    //Setting page load time out		driver.manage().timeouts().pageLoadTimeout(pageTimeOutWait, TimeUnit.SECONDS);
    		return driver;
    	}
    	
    	// Wait for Element to be clickable
    	public WebElement waitForElement(WebElement element) {
    		try{
    			WebDriverWait wait = new WebDriverWait(driver, pageElementLoadWait);
    			wait.until(ExpectedConditions.elementToBeClickable(element));
    			Log.info("Wait for element "+ elementDetails.get(element));
    			return element;
    		}catch(Exception e){
    			
    		}
    		return null;
    	}
    	
    	// Navigate to URL
    	public void navigateToURL(String siteURL) {
    		driver.navigate().to(siteURL);
    		report.logStatus(LogStatus.PASS, "OpenBrowser: " + Config.getProp().getProperty("selenium.browser") + "," + siteURL,
    				"Passed");
    		Log.info("Demo URL Launched");
    	}
    	
    	// Take a screenshot
    	public void takeScreenShot(String fileName) {
    		try {
    			File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
    			FileUtils.copyFile(screenshot, new File(Constants.sScreenshotFilepath + fileName + ".jpeg"));
    			Log.info("Screenshot captured");
    		} catch (Exception e) {
    			System.out.println(e.getMessage());
    			Log.info("Screenshot exception");
    		}
    	}
    	
    	// End Extent Reporting
    	public void endReport() {
    		report.endTest();
    	}
    	
    	// End a Test
    	public void endTest() {
    		driver.close();
    		driver.quit();
    	}
    	
    	/*Click Element method.
    	This method will log in Extent Report as well in Log4j.*/
    	public boolean clickElement(WebElement element) {
    		try {
    			element = waitForElement(element);
    			if (element != null) {
    				takeScreenShot(elementDetails.get(element));
    				report.logStatus(LogStatus.PASS, "Click",
    						"Click " + elementDetails.get(element) + " Success");
    				report.screenshotLog(LogStatus.PASS, "Click Success " + elementDetails.get(element),
    						Constants.sScreenshotFilepath + elementDetails.get(element) + ".jpeg");
    				Log.info("Element Clicked: "+elementDetails.get(element));
    				element.click();
    				return true;
    			}
    			else
    				throw new NoSuchElementException("Element not found");
    		} catch (Exception e) {
    			takeScreenShot("Fail_" + Constants.dateTag);
    			report.screenshotLog(LogStatus.FAIL, "Click Failed ",
    					Constants.sScreenshotFilepath + "Fail_" + Constants.dateTag + ".jpeg");
    			Log.info("Element Click Exception");
    			report.endTest();
    			throw new NoSuchElementException("Element not found");
    		}
    	}
    	
    	/*Enter text in text box method.
    	This method will log in Extent Report as well in Log4j.*/
    	public boolean EnterText(WebElement element, String input) {
    		try {
    			element = waitForElement(element);
    			if (element != null) {
    				element.sendKeys(input);
    				report.logStatus(LogStatus.PASS, "Enter Text",
    						"Enter Text into " + elementDetails.get(element) + " Success");
    				Log.info("Enter text: " + elementDetails.get(element));
    				return true;
    			}else
    				throw new NoSuchElementException("Element not found");
    		} catch (Exception e) {
    			takeScreenShot("Fail_" + Constants.dateTag);
    			report.screenshotLog(LogStatus.FAIL, "Enter Text",
    					Constants.sScreenshotFilepath + "Fail_" + Constants.dateTag + ".jpeg");
    			Log.info("Enter text Exception");
    			throw new NoSuchElementException("Element not found");
    		}	
    	}
    	
    	/*Select a value from drop down by text method.
    	This method will log in Extent Report as well in Log4j.*/
    	public boolean SelectElementByText(WebElement element, String input) {
    		try {
    			element = waitForElement(element);
    			if (element != null) {
    				Select dropdown = new Select(element);
    				dropdown.selectByVisibleText(input);
    				report.logStatus(LogStatus.PASS, "Select Element", "Select Element For " + elementDetails.get(element)
    						+ " Success");
    				Log.info("Select element: " + elementDetails.get(element));
    				return true;
    			}else
    				throw new NoSuchElementException("Element not found");
    		} catch (Exception e) {
    			takeScreenShot("Fail_" + Constants.dateTag);
    			report.screenshotLog(LogStatus.FAIL, "Select Element",
    					Constants.sScreenshotFilepath + "Fail_" + Constants.dateTag + ".jpeg");
    			Log.info("Select element Exception ");
    			throw new NoSuchElementException("Element not found");
    		}
    	}
    }
    
  • The above ‘BasePage’ class file has a constructor which creates an instance of the Page Object using ‘PageFactory’ class. It also has a method to initialize the required WebDriver, wait for an element to be clickable, navigate to an URL, take a screenshot and end the test. It also contains different action methods like clicking a web element, entering a text in a text box, selecting a value from a drop down. The action methods not only completes an action on a web element but also logs the information in the report and log file. Notice that we are declaring a HashMap (Collection in Java) public static HashMap<WebElement, String> elementDetails = new HashMap<WebElement, String>(); to store and name the web elements. We will use it to name the screenshot images.
  • Next task is to create Page Objects. Here we will create two Page Objects. This is similar to what we covered in Part 1: Page Object Model…. Right click on the ‘src/main/java‘ folder and click New -> Package. Name the package as ‘com.testmadness.pages’ and click Finish to close the window. Inside ‘com.testmadness.pages’ package, we will place our Page Objects.
  • Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘HomePage.java’. This ‘HomePage’ class will extend our ‘BasePage’ class. In this class, we will place all the Home Page element locators and methods. Click Finish to close the window. Copy and paste below code into ‘HomePage’ class.
    package com.testmadness.pages;
    
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import com.testmadness.base.BasePage;
    import com.testmadness.utils.Log;
    
    public class HomePage extends BasePage {
    	
    	
    	public HomePage(WebDriver driver) {
    		super(driver);
    		
    	}
    
    	// Element Locators for username textbox
    	@FindBy(name = "userName")
    	private WebElement userName;
    	public WebElement getUserName(){
    		try{
                            // Store and name the web element into HashMap
    			elementDetails.put(userName, "User Name text box");
    		}catch(Exception e){
    			
    		}
    		return userName;
    	}
    
    	// Element Locators for password textbox
    	@FindBy(name = "password")
    	private WebElement password;
    	public WebElement getPassword(){
    		try{
                            // Store and name the web element into HashMap
    			elementDetails.put(password, "Password text box");
    		}catch(Exception e){
    			
    		}
    		return password;
    	}
    
    	// Element Locators for login button
    	@FindBy(xpath = "//input[@name='login']")
    	private WebElement login;
    	public WebElement getLogin(){
    		try{
                            // Store and name the web element into HashMap
    			elementDetails.put(login, "Login button");
    		}catch(Exception e){
    			
    		}
    		return login;
    	}
    
    	// Login method. Notice there is no return type
    	public FlightFinderPage loginUser(String uname, String pwd) {
    		// Enter username
    		EnterText(getUserName(), uname);
    		// Log4j logging
    		Log.info("HomePage.loginUser - username entered");
    
    		// Enter password
    		EnterText(getPassword(), pwd);
    		// Log4j logging
    		Log.info("HomePage.loginUser - password entered");
    
    		// Click Login button
    		clickElement(getLogin());
    		// Log4j logging
    		Log.info("HomePage.loginUser - login button clicked");
    	
                    // Returns an object of FlightFinderPage class
    		return new FlightFinderPage(driver);
    	}
    }
    
  • We will create one more Page Object. Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘FlightFinderPage.java’. All our Page Object will extend the ‘BasePage’ class. Copy and paste below code into ‘FlightFinderPage’ class.
    package com.testmadness.pages;
    
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import com.testmadness.base.BasePage;
    import com.testmadness.utils.Log;
    
    
    public class FlightFinderPage extends BasePage {
    
    	public FlightFinderPage(WebDriver driver) {
    		super(driver);
    	}
    
    	//Element Locators for one way radio button
    	@FindBy(xpath = "//input[@value='oneway']")
    	private WebElement onewayRadio;
    
    	public WebElement getOneWayRadio() {
    		try {
                            // Store and name the web element into HashMap
    			elementDetails.put(onewayRadio, "One Way radio button");
    		} catch (Exception e) {
    
    		}
    		return onewayRadio;
    	}
    	
    	//Select the location from dropdown
    	@FindBy(name = "fromPort")
    	private WebElement fromPortDrop;
    	public WebElement getFromPortDrop(){
    		try{
                            // Store and name the web element into HashMap
    			elementDetails.put(fromPortDrop, "From Port drop down");
    		}catch (Exception e) {
    
    		}	
    		return fromPortDrop;
    	}
    	
    	//Select the day from dropdown
    	@FindBy(xpath = "//select[@name='fromDay']")
    	private WebElement fromDayDrop;
    	public WebElement getFromDayDrop(){
    		try{
                            // Store and name the web element into HashMap
    			elementDetails.put(fromDayDrop, "From Day drop down");
    		}catch (Exception e) {
    
    		}
    		return fromDayDrop;
    	}
    	
    	//Click Business radio button
    	@FindBy(xpath = "//input[@value='Business']")
    	private WebElement businessRadio;
    	public WebElement getBusinessRadio(){
    		try{
                            // Store and name the web element into HashMap
    			elementDetails.put(businessRadio, "Business radio button");
    		}catch (Exception e) {
    
    		}
    		return businessRadio;
    	}
    	
    	//Click find flights button
    	@FindBy(name = "findFlights")
    	private WebElement findFlightsButton;
    	public WebElement getFindFlightsButton(){
    		try{
                            // Store and name the web element into HashMap
    			elementDetails.put(findFlightsButton, "Find Flights button");
    		}catch (Exception e) {
    
    		}
    		return findFlightsButton;
    	}
    
    
    	//Find Flights method. Notice there is no return type
    	public void findFlights(String departFrom, String departDate) {
    		// Click one way radio button
    		clickElement(getOneWayRadio());
    		Log.info("FlightFinderPage.findFlights - One way Radio Button clicked");
    		// Select Departing From dropdown
    		SelectElementByText(getFromPortDrop(), departFrom);
    		Log.info("FlightFinderPage.findFlights - Depart From Dropdown clicked");
    
    		// Select Departing Day dropdown
    		SelectElementByText(getFromDayDrop(), departDate);
    		Log.info("FlightFinderPage.findFlights - Depart Date Dropdown clicked");
    
    		// Click business class
    		clickElement(getBusinessRadio());
    		Log.info("FlightFinderPage.findFlights - Business Radio Button clicked");
    
    		// Click Find Flights button
    		clickElement(getFindFlightsButton());
    		Log.info("FlightFinderPage.findFlights - Find Flights Button clicked");
    
    	}
    
    }
    
  • Our next task is to add the data sheet using which we will input the test data to our test scripts. We will take the same approach as we did in Data Driven Framework Development….
  • Right click on the ‘src/main/java‘ folder and click New -> Package. Name the package as ‘com.testmadness.data’ and click Finish to close the window. Inside the ‘com.testmadness.data’ package, we will place our test data file in ‘xlsx’ format. Always remember to format all cells in the excel sheet to text format. Our test data sheet will look as displayed below.

    Data.xlsx file for storing test data
  • This is how our ‘src/main/java‘ folder look like once we reach here.

    ‘src/main/java’ folder

With this, we have completed the creation of Page Objects. Here we created only two Page Objects. Best practice is to create one Page Object per page in the test application.

In the next post, we will be creating actual scripts in our Hybrid Framework. Till then, Happy Learning!

Part 2: Hybrid Framework Development…

In Part 1: Hybrid Framework Development…, we have created overall structure of our framework. Our next step is to create source codes inside our project. Here also we will take a step by step approach in creating the source code.

  • First, delete the unwanted packages from the ‘src/main/java‘ and ‘src/test/java’ folders. Remember, Maven by default creates some test classes. In our case, we don’t need them as we will be creating our own.
  • Right click on the ‘src/main/java‘ folder and click New -> Package. Name the package as ‘com.testmadness.utils’ and click Finish to close the window.
  • Inside ‘com.testmadness.utils’ package, we will place all the utility classes which are basically common classes and not project specific. For example, we need some class for the Log4j, Extent Reports, accessing the property file etc..
  • First of all, we will create a ‘Constants.java’ file to store all the constant values. Right click on ‘com.testmadness.utils’ package and select New -> Class. Name the class as ‘Constants.java’. Copy the below code into ‘Constants.java’ file. In this class, we will be storing different constants that will be referred from different classes. Notice that all our constants are static variables.
    package com.testmadness.utils;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class Constants {
    
    	// TestResults folder location
    	public static final String PATH_RESULTS = System.getProperty("user.dir") + "/TestResults/";
    
    	// Test Data Excel location
    	public static final String PATH_DATA = System.getProperty("user.dir") + "/src/main/java/com/testmadness/data/Data.xlsx";
    
    	// Test Data Excel Sheet name
    	public static final String SHEETNAME = "Sheet1";
    
    	// Test Data Excel File Name
    	public static final String FILE_NAME = "TestData.xlsx";
    
    	// Property file name
    	public static final String PROP_NAME = "system.properties";
    
    	// Property file location
    	public static final String PROP_LOCATION = "src/main/resources";
    
    	public static Date date = new Date();
    	public static SimpleDateFormat reportDate = new SimpleDateFormat("MM-dd-yyyy hh-mm-ss");
    	
    	//Log Files Constants
    	
    	public static String logFileName = "LOG_"+reportDate.format(date);
    	public static String dateTag = reportDate.format(date);
    	public static String logFolderName = "LOG_FOLDER_"+reportDate.format(date);
    
    	// Extent Report constants
    	public static final String filePath = System.getProperty("user.dir");
    	public static final String reportPath = filePath + "/RESULT_LOG";
    	public static final String imagePath = filePath + "/IMAGES";
    
    	public static final String reportFileName = reportPath + "/" + "TESTREPORT_" + reportDate.format(date) + "TC.html";
    	public static final String screenshotFileName = reportPath + "/SCREENSHOTS" + "TESTREPORT_"
    			+ reportDate.format(date) + "TC.html";
    	public static final String screenshotFilePath = screenshotFileName + "/SCREENSHOTS" + "TEST"
    			+ reportDate.format(date) + "/";
    	public static String sScreenshotFilepath = filePath + "/Screenshots/" + "IOLS_Screenshot_" + reportDate.format(date)
    			+ "/";
    	public static String sReportFileName = reportPath + "/" + "IOLSTestReport_" + reportDate.format(date) + ".html";
    
    }
    
  • Next, we will create a class to access the property file. Right click on ‘com.testmadness.utils’ package and select New -> Class. Name the class as ‘Config.java’. Copy the below code into ‘Config.java’ file.
    package com.testmadness.utils;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.util.Properties;
    
    public class Config {
    
    	private static Properties prop;
    	public static Properties getProp() {
    		if (prop == null) {
    			prop = new Properties();
    			InputStream input = null;
    			try {
    				//Two arguments passed for property file location
    				input = new FileInputStream(new File(Constants.PROP_LOCATION, Constants.PROP_NAME));
    				prop.load(input);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    		return prop;
    	}
    }
    
  • Next, we will create Log class and log4j.xml files. We can directly copy the codes from Working with Log4j logging…. We need only codes from’Log.java‘ and ‘log4j.xml‘ files. Notice that both these files will be placed inside ‘com.testmadness.utils’ package. However ‘log4j.xml‘ file has small changes in order to get the log folder and log files names. Copy the below code and copy in ‘log4j.xml‘ file.
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
    <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
    	<!-- Appender for printing in console -->
    	<appender name="console" class="org.apache.log4j.ConsoleAppender">
    		<layout class="org.apache.log4j.PatternLayout">
    		    <!-- %d{yyyy-MM-dd HH:mm:ss} refers to Date format 2017-03-23 15:54:44 INFO  Log:15 -->
    		    <!-- %-5p refers to Type of logging INFO-->
    		    <!-- %c{1}  -->
    			<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
    		</layout>
    	</appender>
    	<!-- Appender for printing in file -->
    	<appender name="file" class="org.apache.log4j.RollingFileAppender">
    		<!-- Appending false -->
    		<param name="append" value="false" />
    		<!-- Maximum 10kb size. New file will be created and old file renamed -->
    		<param name="maxFileSize" value="10KB" />
    		<!-- Maximum 5 log files. Old ones will be deleted -->
    		<param name="maxBackupIndex" value="5" />
    		<!-- Location of log file -->
    		<param name="file" value="test-output/log/${logfoldername}/${logfilename}.log" />
    		<layout class="org.apache.log4j.PatternLayout">
    			<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
    		</layout>
    	</appender>
    
    
    	<root>
    	<!-- Setting Logging level  -->
    		<level value="info" />
    		<!-- Setting logging level for console -->
    		<appender-ref ref="console" />
    		<!-- Setting logging level for file -->
    		<appender-ref ref="file" />
    	</root>
    </log4j:configuration>
    
  • For advanced HTML reports, we will use Extent Reports. Right click on ‘com.testmadness.utils’ package and select New -> Class. Name the class as ‘Reports.java’. Copy the code from Advanced HTML Reports… post. However, a small update can be made on the Report location as we can refer the path already mentioned in our Constants.class.
  • Our next task is to create a ‘ExcelUtil.class’ inside ‘com.testmadness.utils’ package. This class will help us to access the test data entered in an Excel sheet. Copy the code of ExcelUtil.class’ from Data Driven Framework Development… post.
  • So far our ‘com.testmadness.utils’ package has classes for accessing property file, Log4j, Excel sheet, Constants and for generating HTML reports.
  • The final structure of ‘com.testmadness.utils’ package will look like as displayed below.

    com.testmadness.www package

 

In the next post, we will focus mainly on creating Page based objects. So stay tuned!

Part 1: Hybrid Framework Development…

So far we have covered multiple topics in Selenium Automation. And the good thing is that we are going to use most of the topics covered into the creation of our Hybrid Framework. Sounds interesting, right?

What all functionality our Hybrid Framework will contain?

  • First and foremost it will be a Maven project. We will not download individual jars as we did earlier, rather we will add dependencies in our pom.xml file.
  • And of course, it will a Selenium, TestNG project.
  • We will include like Page Object Modeling, Page Factory concepts in our framework.
  • We will add Data Driven technique in our framework for test data input.
  • The framework will have Log4j logging.
  • In addition to TestNG reports, we will also include Extent Reports for advanced HTML reports.
  • We will create user defined methods for different actions.
  • And we will also add a new topic in our Hybrid Framework and that is the use of properties file in order to store some configurable parameters of our application.

Setting up this framework will be a long and tiring affair but for sure it’s going to be an interesting one. Our demo application will be New Tours Demo, which we used in our previous posts also.

Hybrid Framework Development:

  • Our first task is to create a Maven project in Eclipse. Go to File -> New -> Project and select Maven and click Next. Use default workspace location and quick-start archetype. Enter a Group ID, in our case its ‘com.testmadness.www’, Artifact ID as ‘HybridFramework’. Click Finish to complete the project creation. Once complete, Maven will automatically create the project structure as displayed below.

    Maven Project
  • Next step is to add the dependencies in our pom.xml file. For that we will need dependencies for Selenium, TestNG, Log4j, Apache POI and Extent Reports. We can get the dependencies either by doing a search in Google or by directly going to Maven Repository website. After successful update, our pom.xml will look as below:
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    
    	<groupId>com.testmadness.www</groupId>
    	<artifactId>HybridFramework</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<packaging>jar</packaging>
    	<name>HybridFramework</name>
    	<url>http://maven.apache.org</url>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<maven.compiler.source>1.8</maven.compiler.source>
    		<maven.compiler.target>1.8</maven.compiler.target>
    		<selenium.version>3.4.0</selenium.version>
    		<testng.version>6.10</testng.version>
    		<log4j.version>1.2.17</log4j.version>
    		<extent.version>2.41.2</extent.version>
    		<selenium.browser>CHROME</selenium.browser>	
    <webdriver.safari.driver>/usr/bin/safaridriver</webdriver.safari.driver>
    <webdriver.firefox.driver>/Users/sejijohn/Documents/JARS/geckodriver</webdriver.firefox.driver>		<webdriver.chrome.driver>/Users/sejijohn/Documents/JARS/chromedriver 2</webdriver.chrome.driver>
    		<logfoldername>logfoldername</logfoldername>
    		<logfilename>logfilename</logfilename>
    	</properties>
    
    <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>properties-maven-plugin</artifactId>
            <version>1.0.0</version>
            <executions>
              <execution>
                <phase>generate-resources</phase>
                <goals>
                  <goal>write-project-properties</goal>
                </goals>
                <configuration>
                  <outputFile>
                    src/main/resources/system.properties
                  </outputFile>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.seleniumhq.selenium</groupId>
    			<artifactId>selenium-java</artifactId>
    			<version>${selenium.version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.testng</groupId>
    			<artifactId>testng</artifactId>
    			<version>${testng.version}</version>
    		</dependency>
    		<dependency>
    			<groupId>log4j</groupId>
    			<artifactId>log4j</artifactId>
    			<version>${log4j.version}</version>
    		</dependency>
    		<dependency>
    			<groupId>com.relevantcodes</groupId>
    			<artifactId>extentreports</artifactId>
    			<version>${extent.version}</version>
    		</dependency>
                    <dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi</artifactId>
    			<version>3.16</version>
    		</dependency>
                    <dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi-ooxml</artifactId>
    			<version>3.16</version>
    		</dependency>
    	</dependencies>
    </project>
    

In the above pom.xml file, the first part (represented in pink color) has the project details like, Group ID, Artifact ID which we entered in the initial stage of our project creation. Since we selected default archetype, the packaging is in jar.

The second part (represented in blue color) is where we are setting the properties. If we want to use Java 8 language features (-source 1.8) and also want the compiled classes to be compatible with JVM 1.8 (-target 1.8), we can add the two following two properties, maven.compiler.source, maven.compiler.target with the Java version mentioned in them. The different version properties that we set, for example, selenium.version can be used to set the version of different dependencies inside pom.xml. This is not mandatory as we can directly set the versions in the dependencies. However, by doing this, we can add extra flexibility to directly update the versions from properties rather updating in the dependencies. Other properties such as selenium.browser, webdriver.safari.driver, are used to pass configurable parameters to a .properties file, which we will create shortly. Please make sure to download latest Safari, Chrome, Firefox drivers, and store in a local location. From properties we will be passing the location of different drivers to our code. The next ones are logfoldername, logfilename properties which we are using to set the location of our log files in log4j.xml.

The third part (represented in purple color) is a plugin to update the properties from pom.xml file to a .properties file inside ‘src/main/resources’ folder.

The last part (represented in red color) is where we are adding the dependencies. As mentioned earlier, we can get the dependencies and their versions by doing a Google search.

  • After updating the pom.xml file, our next step is to create a .properties file. We will use this .properties file to configure the type of browser and as well as for setting the browser properties. Always remember, that we should place the .properties file inside ‘src/main/resources’ folder and not into ‘src/main/java’ directory (keeping the same subfolder structure). For that, we need to create a source folder named resources inside ‘src/main‘ directory.
  • To create a source folder, click on Project name, select New -> Source Folder. Provide the folder name as ‘src/main/resources’.

    Source Folder creation
  • After creating resources folder, our next step is to create a .properties file. Right click on ‘src/main/resources’ folder -> New -> Other. Select File and click Next. Name the file as ‘system.properties’ and click Finish. A blank .properties file will be created inside resources folder.

    system.properties files inside resources folder
  • Now that we have updated pom.xml file and created .properties file, our next step is to update our Maven project with these changes. To update project, click on Project Name -> Maven -> Update Project.

    Update Maven project
  • After successful update, the Java System Library is updated from default 1.5 version to 1.8 version based on maven.compiler.source, maven.compiler.target properties in the pom.xml file. Also the system.properties file inside resources folder is automatically updated with all the properties from pom.xml file.

    system.properties file

With this, we have completed the initial structure of our Hybrid Framework. Our next step is to add the source codes inside our Framework. We will cover this in our next topic. Till then, Live Well and Happy Learning!

Part 2: Introduction to Maven…

In this post, we will create a simple Maven project using Eclipse as IDE. Eclipse provides excellent support for creating Maven projects. We will take a step by step approach to create a TestNG project using Maven.

Install Maven in Eclipse:

  • Normally Eclipse comes with Maven plugin installed. We can confirm by clicking File -> New -> Project. In the new project wizard search for ‘Maven’. If Maven is already installed in IDE, then it will be listed as below.

    Maven Project
  • If Maven is not available, then we have to go to Eclipse Marketplace to get the plugin. From the top Menu bar, click Help -> Eclipse Marketplace.

    Eclipse Marketplace
  • Click Eclipse Marketplace to open up the wizard. Type in ‘Maven’ and click Go, to search for Maven plugins. We have to select and install ‘Maven Integration for Eclipse’ plugin. In below case, since Maven is already installed, it will show as ‘installed’.

    Install Maven plugin

Maven Project Explanation:

  • After successful installation, our next step is to create a TestNG project in Eclipse using Maven.
  • Go to File -> New -> Other. In the wizard, type in ‘Maven’. If Maven is installed then it will be displayed.
  • Select Maven Project and click Next. We have the option to provide a new workspace location or else use the default one. We also have an option to create a simple project by skipping the archetype selection. For now, we will select our own archetype.

    New Maven project
  • What is an archetype in Maven? Archetype is a plugin in Maven which will first ask to choose a type of archetype from the internal catalog and then generate a project template based on archetype selection. The type of archetype selection depends on the type of project that we need to build. For example if its a simple Java Project, then we will use ‘maven-archetype-quickstart’ archetype and if it’s a Java Web Application project then we will use ‘maven-archetype-webapp’ archetype. Thus in short Maven will create all the required folders, download all required classes and files based on archetype selection.
  • In this project, we will be selecting quick-start archetype. Click next to proceed.

    Quick start archetype
  • Based on the selection of quick-start archetype, Maven will create a project structure inside our project.

    Maven project structure
  • Enter Group ID which is the package name, Artifact ID which is Project folder name. Click Finish to complete the project creation.

    Maven project
  • As we click Finish, Maven automatically creates the project structure and download all the necessary jars based on our archetype selection. As displayed below, inside project folder, there is a ‘src’ folder and which has ‘main’ and ‘test’ folders. ‘main’ folder will contain source code for the application and ‘test’ folder will contain source code for tests. Always remember Maven identifies a test file which has ‘Test’ added as a suffix. Like in below case, Maven by default creates two java files, one in ‘main’ folder and another in ‘test’ folder. The java file inside ‘test’ folder is named as ‘AppTest.java’.

    Maven project structure
  • As mentioned earlier, Maven not only creates project structures but also download all necessary jars for the archetype selected. If we expand ‘Maven Dependency’, we can see that Maven automatically downloads the JUnit jar and stores it in ‘.m2’ folder, which is on our local machine.

    Maven Dependency
  • Now, what is ‘.m2’ folder? ‘.m2’ folder is created automatically when we create our first Maven project. This folder is called local repository and contains a settings.xml file that contains global settings for all maven executions and a folder called repository that holds all of the local copies of various maven artifacts, either caches of artifacts pulled down from remote repositories, such as Maven Central, or artifacts built by your local maven builds. The artifacts are organized in there in folder structures that mirror the groupId’s of the artifacts. In Windows OS, it will be placed in ‘C://Users/Username/.m2’ path and in Mac OS, it will be placed in ‘/Users/username/.m2’ path. In Mac OS, by default folder starting with ‘.’ are hidden. To display the hidden folders in Mac OC, type in the following command in Terminal and hit Enter.
    -- Display hidden folders in Mac OS --
    defaults write com.apple.Finder AppleShowAllFiles YES
    
  • settings.xml file is not required unless we want to change the default settings. We can create our own settings.xml file and place in our ‘.m2’ directory. To know more details on settings.xml file, visit Settings.xml page.
  • Next, comes the target directory. It is created by Maven and contains all the compiled classes, JAR files etc.
  • The most important part of a Maven project is pom.xml file. A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details used by Maven to build the project. It contains default values for most projects. Examples for this is the build directory, which is target; the source directory, which is src/main/java; the test source directory, which is src/test/java; and so on. All POMs extend the Super POM unless explicitly set. For more details on pom.xml file, visit pom.xml page.

Add TestNG class into Maven Project:

  • Our next task is to add a TestNG class and execute it as a Maven project.
  • For adding a TestNG class, we will need required jars as mentioned in our previous post Part 1: Scripting using TestNG…. However, in a Maven project, we don’t have to manually upload the jars. We will just update the pom.xml file to include the TestNG dependency.
  • To get a Maven dependency for any jar, we can directly Google it. Go to Google and type in ‘testng maven dependency’ and hit enter. Else we can directly go to mvn repository testng.
  • Copy the below code and paste it inside dependencies tag in pom.xml file. Note that we are using TestNG version 6.10.
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.10</version>
    </dependency>
    
  • This is how our updated pom.xml file looks like.

    Updated pom.xml file
  • Once we save the pom.xml file, Maven automatically downloads the required jars and stores them in our local repository. If we expand the Maven dependency, we can see the jar location.

    Maven dependencies
  • To create a TestNG class, we will follow the same steps and reuse the same code mentioned in our previous post Part 1: Scripting using TestNG…. One point to remember is that the TestNG class is actually a test script, hence we have to place it in projectname/src/test/java folder. And also as mentioned before we have to add ‘Test‘ to the file name. Only then Maven will execute the test script.
  • Next step is to execute our TestNG script. Now there are many ways to execute the Maven Project. One way is to directly open the Command Prompt or Terminal and execute the below command. The commands are same for Windows OS and Mac OS.
    <!-- Go to Project folder -->
    cd /users/sejijohn/documents/workspace/TestMaven
    
    <!-- First clean and the run test-->
    mvn clean test 
    
  • This maven command will first delete all the class files from the target folder, recompile all the class file and then run the test. The final result will be displayed in the Command Prompt or Terminal.

    Terminal
  • Another way is to execute using Eclipse. Right click Project Name and click Run As -> Maven Test.

    Run as Maven test
  • Maven is based around the central concept of a build lifecycle. When we select Maven test, Maven actually executes all lifecycle phases above test in a sequential manner, which means it will first execute validate, followed by compile and then test. To know more Maven lifecycle, visit Maven lifecycle page.

With this, we completed our first Maven Project creation in Eclipse. Learning Maven is no easy task but it will help you in coming posts. Hope you were able to follow most of it. Till next post, Happy Learning!

Part 1: Introduction to Maven…

Apache Maven is one important tool that we will be using in creating every single Selenium project from here. In short its a software project management and comprehension tool. It allows the developer to automate the process of creating the initial folder structure for Java application, performing the compilation, testing, packaging and deploying the final product. It is implemented in Java which makes it platform-independent. Java is also the best work environment for Maven. Some of the key feature of Maven are:

  • Convention over Configuration: Maven uses Convention over Configuration which means developers are not required to create build process themselves. Maven provides sensible default behavior for projects.
  • Dependency management: Maven encourages the use of a central repository of JARs and other dependencies. Maven comes with a mechanism to download any JARs required for building your project from a central JAR repository.
  • Repository: Project dependencies can be downloaded from local file system, from Internet or public repositories like Maven Central.
  • Extensible via plug-ins: The Maven build system is extensible via plug-ins, which allows to keep the Maven core small. The Maven core does for example not know how to compile Java source code, this is handled by the compiler plug-in.

We have to go through following topics like Maven Lifecycle, POM and Super POM, Standard Directory Layout, Repositories, Dependency mechanism, Plugins etc. to gain more knowledge on Maven. Complete Maven documentation is placed in Apache Maven Documentation .

Download and Install Maven

The first step is to download and install Maven in your machine. We need to follow below steps for installing Maven.

  1. Before installing Maven, we have to ensure that JDK is already installed in our machine and path is also set. All the steps for installing JDK is mentioned in Installing Java Development Kit… and setting path is mentioned in Setting up Environment Variables….
  2. Open Browser and navigate to Google. Type in “Maven download” and go to downloads page in maven.apache website. Direct link to Maven download page Maven Download.
  3. Click on the Binary zip archive link to start the download in the local machine. This is same for Windows and Mac machines.

    Click Maven zip link to start download
  4. Once download is complete in the local Downloads folder of our machine, we have to move the ‘apache-maven-3.5.0’ folder to a location which is consistent with other applications. For Mac machine, we can copy and paste the folder in your home folder, e.g. ‘/Users/sejijohn’ location. We can also use the Terminal in Mac to move the folder from Downloads to home folder. We can use the below command in Terminal to move the ‘apache-maven-3.5.0’ folder to Home. This command will require password authentication.
    --sudo mv source_folder destination_folder--
    sudo mv /Users/sejijohn/Downloads/apache-maven-3.5.0/ /Users/sejijohn
    

    For Windows machine, we can paste the ‘apache-maven-3.5.0’ folder inside C Drive ‘Program Files/Apache’ folder.

  5. Next step is to set the environment variables for Maven. For Mac OS, Open Terminal and type in the following command  “vim .bash_profile” and then type ‘i’ to insert into the VIM Editor. We need to set M2_Home and MAVEN_HOME variables and then update existing PATH variable. Type in below commands in the VIM editor.
    --Set up M2_HOME--
    export M2_HOME=/Users/sejijohn/apache-maven-3.3.9
    
    --Set up MAVEN_HOME--
    export MAVEN_HOME=/Users/sejijohn/apache-maven-3.3.9
    
    --Append existing PATH--
    export PATH=$PATH:/Users/sejijohn/apache-maven-3.3.9/bin
    
    MAC Terminal

    To Save the entires in VIM Editor, hit Escape, type in: “:wq + Enter”.
    To Quit without saving the editor, hit Escape, type in “:q!”. To save the entries in .bash_profile, use below command.

    --Save the entries in .bash_profile--
    source .bash_profile
  6. For Windows OS, type “advanced system settings” in the search box (beside the Windows start button), click View advanced system settings. Select Advance tab and click Environment Variables button. To run maven from command line in Windows we have to set MAVEN_HOME, M2_HOME and PATH variables. In System variables, add a new ‘MAVEN_HOME’ variable and point it to the Maven folder inside C Drive and that is “C:Program FilesApacheapache-maven-3.5.0”. Add another variable ‘M2_HOME’ and point it to same Maven location in C Drive.

    Notice MAVEN_HOME and M2_HOME pointing to same location where Apache Maven folder placed.
  7. We need to update the PATH variable.For that we have to point it to bin folder inside Apache Maven folder.

    Notice PATH is updated to add the Maven bin folder
  8. Once the environment variables settings are completed, next step is to verify if the maven installation is successful. For that go to Mac Terminal or Windows Command Prompt and type in ‘mvn -version‘. If Maven installation is successful, we will see the below message.
    mvn -version in Terminal Mac OS

    With this we have completed the steps to install and configure Maven in Windows and Mac machines. In the next post we will create a sample Maven Project using Eclipse. Till then, Happy Learning!

Advanced HTML Reports…

TestNG default reports are not very attractive and they are very basic. In this post we will use an advanced HTML Report called Extent Report. We will use this reporting to create our own custom report. Below is a snapshot on how our custom report will look like.

Extent Reports

To save time and effort we will be using already created Java Project ‘NewTourPageFactory‘ from our previous post Working with Page Factory…. We will add an additional class for our reporting and also update some existing classes. Below is the snapshot of the project structure of ‘NewTourPageFactory‘.

NewTourPageFactory Project Structure
  1. To start with Extent Reporting, first task is to download the Jar. Go to Google and type in “Extent Report jar download”. Click on the maven repository link to download the Jar or else you can go directly to Extent Report Jar download link to download the Jar. Here we will be using version 2.40.2.
  2. Extent reports jar has a dependency with freemarker jar. So this too need to be downloaded. Here we are using ‘freemarker-2.3.23’ version.
  3. Once both Jars are downloaded to the local folder, the next step is to add them into our Java Project. Right Click on the project name and click Properties. Click on Java Build Path -> Add External Jars. Go to the local folder where ‘extentreports-2.40.2’ and ‘freemarker-2.3.23’ are saved. Add Jars and click OK to close the window. Once complete, we will see them inside ‘Referenced Libraries‘.

    Extent Report Jars
  4. The next step is to create one Extent Report Reports class. This class will contain all the required methods for starting the test, logging the test status and ending the test. Right click on ‘com.selenium.util‘ package and click New -> Class. Name the class file as ‘Reports‘.

    Reports class
  5. Copy and paste the below code into the Reports class.
    package com.selenium.util;
    
    import com.relevantcodes.extentreports.ExtentReports;
    import com.relevantcodes.extentreports.ExtentTest;
    import com.relevantcodes.extentreports.LogStatus;
    
    public class Reports {
    
    	public static ExtentReports extent;
    	public static ExtentTest test;
    
    	// Start Test Case
    	public void startTest(String testCaseName) {
    
    		/*Create ExtentReports object passing location and report name as argument. Notice a new Result Log folder will be created inside project and the report name will be TestReport.html*/
    		extent = new ExtentReports(System.getProperty("user.dir") + "/RESULT_LOG" + "/TestReport.html");
    
    		// Add details to our report
    		extent.addSystemInfo("Selenium Version", "3.0.1").addSystemInfo("Environment", "QA");
    
    		// Create ExtentTest passing test case name and description
    		test = extent.startTest(testCaseName, "Our First Test Report");
    	}
    
    	// Log Test status, Test name and Test details
    	public void logStatus(LogStatus testStatus, String testStepName, String testDetails) {
    
    		// Log test status
    		test.log(testStatus, testStepName, testDetails);
    	}
    
    	// Capture screenshot and log into report
    	public void screenshotLog(LogStatus logStatus, String testStepName, String screenShotPath) {
    
    		// Attach screenshots
    		test.log(logStatus, testStepName + test.addScreenCapture(screenShotPath));
    	}
    
    	// End Test Case
    	public void endTest() {
    
    		// End test
    		extent.endTest(test);
    		extent.flush();
    		extent.close();
    	}
    }
    
  6. Next steps is to add the logging information into our test steps. For that we will add few steps into our page object class. Here we will be updating ‘HomePage’ class inside ‘com.selenium.pages‘ package.

    HomePage class
  7. Replace the existing code of ‘HomePage’ class with below code.
    package com.selenium.pages;
    
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import com.relevantcodes.extentreports.LogStatus;
    import com.selenium.util.Log;
    import com.selenium.util.Reports;
    
    public class HomePage {
    	// Declare WebDriver
    	WebDriver driver;
    	Reports reports;
    	// Element Locators for username textbox
    	@FindBy(name = "userName")
    	private WebElement userName;
    
    	// Element Locators for password textbox
    	@FindBy(name = "password")
    	private WebElement password;
    
    	// Element Locators for login button
    	@FindBy(name = "login")
    	private WebElement login;
    
    	// Constructor. Driver Initialization
    	public HomePage(WebDriver driver) {
    		this.driver = driver;
    		reports = new Reports();
    
    	}
    
    	// Login method. Notice there is no return type
    	public void loginUser(String uname, String pwd) {
    		// Enter username
    		userName.sendKeys(uname);
    		// Log4j logging
    		Log.info("HomePage.loginUser - username entered");
    
    		// Extent Reprot Logging. Enter Log status for HTML report
    		reports.logStatus(LogStatus.PASS, "Enter user name", "User name entered" + " <span class='label success'> Success</span>");
    		// Enter password
    		password.sendKeys(pwd);
    		// Log4j logging
    		Log.info("HomePage.loginUser - password entered");
    
    		// Extent Reprot Logging. Enter Log status for HTML report
    		reports.logStatus(LogStatus.PASS, "Enter password", "Password entered" + " <span class='label success'>Success</span>");
    		// Click Login button
    		login.click();
    		// Log4j logging
    		Log.info("HomePage.loginUser - login button clicked");
    
    		// Extent Reprot Logging. Enter Log status for HTML report
    		reports.logStatus(LogStatus.PASS, "Click Login","Login button clicked" + " <span class='label success'> Success</span>");
    	}
    }
    
  8. In the above code, notice that we created an object of Reports class and used its methods for logging test status. We can log different test status like LogStatus.PASS, LogStatus.FAIL, LogStatus.ERROR etc based on our test.
  9. We can take same approach for other page object classes. However in this post we will be updating only the ‘HomePage’ class.
  10. Before logging the log status in extent report, we need to start the test. This will be done in the TestNG test case.

    BookFlight class – TestNG test case
  11. We will update the ‘BookFlight’ class inside ‘com.selenium.testcase‘ package with below code.
    package com.selenium.testcase;
    
    import org.testng.annotations.Test;
    import com.selenium.pages.BookFlightPage;
    import com.selenium.pages.FlightConfirmationPage;
    import com.selenium.pages.FlightFinderPage;
    import com.selenium.pages.HomePage;
    import com.selenium.pages.SelectFlightPage;
    import com.selenium.util.Log;
    import com.selenium.util.Reports;
    import org.testng.annotations.BeforeMethod;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import org.apache.log4j.xml.DOMConfigurator;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    import org.openqa.selenium.support.PageFactory;
    import org.testng.annotations.AfterMethod;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.BeforeTest;
    
    public class BookFlight {
    
    	WebDriver driver;
    	HomePage homePage;
    	FlightFinderPage flightFinderPage;
    	SelectFlightPage selectFlightPage;
    	BookFlightPage bookFlightPage;
    	FlightConfirmationPage flightConfirmationPage;
    	Reports reports;
    
    	// Test Case
    	@Test(dataProvider = "newTourData")
    	public void bookFlight(String uname, String pwd, String departFrom, String departDate, String fname, String lname,
    			String ccNum) {
    
    		/*
    		 * Test case logic.
    		 * 
    		 */
    		Log.startTestCase("Test Case bookFlight");
    		Log.info("BookFlight.bookFlight - Home Page Opens");
    		homePage.loginUser(uname, pwd);
    		Log.info("BookFlight.bookFlight - Flight Finder Page Opens");
    		flightFinderPage.findFlights(departFrom, departDate);
    		Log.info("BookFlight.bookFlight - Select Flight Page Opens");
    		selectFlightPage.reserveFlight();
    		Log.info("BookFlight.bookFlight - Book Flight Page Opens");
    		bookFlightPage.bookFlight(fname, lname, ccNum);
    		Log.info("BookFlight.bookFlight - Flight Confirmation Page Opens");
    		flightConfirmationPage.clickLogOut();
    		Log.endTestCase("Test Case bookFlight");
    
    	}
    
    	// Driver and Page Objects Initialization
    	@BeforeMethod
    	public void beforeMethod() {
    		// Initialize driver
    		driver = new ChromeDriver();
    		Log.info("BookFlight.beforeMethod - Chrome Driver Intialized");
    		// Page Factory Initialization for all page objects
    		homePage = PageFactory.initElements(driver, HomePage.class);
    		flightFinderPage = PageFactory.initElements(driver, FlightFinderPage.class);
    		selectFlightPage = PageFactory.initElements(driver, SelectFlightPage.class);
    		bookFlightPage = PageFactory.initElements(driver, BookFlightPage.class);
    		flightConfirmationPage = PageFactory.initElements(driver, FlightConfirmationPage.class);
    		Log.info("BookFlight.beforeMethod - Page Factory intialization complete");
    
    		// Start Extent Report
    		reports.startTest("Book Flight");
    
    		// Nvaigate to URL
    		driver.get("http://newtours.demoaut.com");
    		Log.info("BookFlight.beforeMethod - open New Tour URL");
    
    		// Maximize window
    		// driver.manage().window().maximize();
    		Log.info("BookFlight.beforeMethod - Browser maximized");
    
    	}
    
    	// Driver closure
    	@AfterMethod
    	public void afterMethod() {
    
    		// End Extent Report
    		reports.endTest();
    
    		// Close and quit the driver to close the Browser
    		driver.close();
    		driver.quit();
    		Log.info("BookFlight.afterMethod - Browser closed");
    	}
    
    	// Create test data
    	@DataProvider
    	public Object[][] newTourData() {
    		return new Object[][] { { "demo", "demo", "London", "7", "john", "Doe", "56465465" } };
    	}
    
    	// Setting System property
    	@BeforeTest
    	public void beforeTest() {
    		// Set System Property for driver location
    		System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
    		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
    		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		// Set System Property for log file location
    		System.setProperty("logfoldername", sdf1.format(Calendar.getInstance().getTime()));
    		System.setProperty("logfilename", sdf2.format(Calendar.getInstance().getTime()) + "-LOG");
    
    		Log.info("BookFlight.beforeTest - System property setting complete");
    		DOMConfigurator.configure("src/log4j.xml");
    		Log.info("BookFlight.beforeTest - XML config complete");
    
    		// Create Extent Report object
    		reports = new Reports();
    	}
    }
    
  12. Notice that in the above code, we are creating a Reports object inside ‘@BeforeTest‘ method, starting the test inside ‘@BeforeMethod‘ and ending the test inside ‘@AfterMethod‘.
  13. Final step is to execute our TestNG test case. Right click on ‘BookFlight’ class and click Run As -> TestNG Test.

    TestNG Test
  14. After successful execution, a new folder named ‘RESULT_LOG’ will be created inside our project and the Extent Report will be placed in this folder. This is because, inside ‘startTest‘ method of Reports class, we are creating an Extent Report object by passing the location as argument.
    /*Create ExtentReports object passing location and report name as
    argument. Notice a new Result Log folder will be created inside
    project and the report name will be TestReport.html*/
    extent = new ExtentReports(System.getProperty("user.dir") + "/RESULT_LOG" + "/TestReport.html");
    
  15. Right click on ‘TestReport.html’ to open on web browser. Below is the snapshots on how our test report will look like.
    Book Flight Extent Report Page 1

    Book Flight Extent Report Page 2
  16. Notice that all the log status that we entered in ‘HomePage’ class are displayed in the HTML report.

Entent Reports help us in creating beautiful looking test reports. They are very easy to implement and also provide valuable information about our test. Hope this post is useful in creating some good looking reports. Many more to follow. Till then, Happy Learning!

Additional Information:

While logging the Extent Report status we have used some HTML code inside ‘HomePage’ class.

// Extent Reprot Logging. Enter Log status for HTML report
reports.logStatus(LogStatus.PASS, "Enter user name",
	"User name entered" + " <span class='label success'>Success</span>");

The span class helps us in providing some meaningful logs and messages into our test report. Some more labels are also available for use.

"<span class='fail label'>Fail</span>"
"<span class='warning label'>Warning</span>"
"<span class='info label'>Info</span>"
"<span class='skip label'>Skip</span>"

 

Design a site like this with WordPress.com
Get started