In our previous post, we staged a way to create Drivers, but we haven’t used it yet. This time we will create some sample tests, a BaseTest.java class, and use TestNG.xml to run them, that way we’ll be able to test if the DriverFactory is working exactly as we expect, and we can execute concurrent or parallel tests, in different browsers, without anything breaking.

Why have a Base Test ?

You should never be initializing / quitting WebDriver instances in your Test Classes, with the main reason being Maintenance and Reusability. If very single test class you write contains something in the lines of :

@BeforeTest
public void initWebDriver() {
	System.setProperty("webdriver.chrome.driver", "path of the exe file\\chromedriver.exe");
	WebDriver driver = new ChromeDriver();
	driver.get("http://www.facebook.com");
}

It is easy to tell this code is unmaintenable, and strongly hardcoded. How do I switch browsers ? Change the application URL ? The path to your driver file? It would require you to navigate through each java class, make the neccesary modifications, and maybe possibly commiting your changes leading to conflicts.

To fix this we’ll create a BaseTest.
The purpose of this class (you can rename if you prefer) is to contain the logic regarding all the steps taken before/after running tests.

To be clear: The only reason to use things like @BeforeTest,@BeforeClass,@AfterMethod, etc. in your Test Class is to satisfy test-specific pre or post-conditions, such as:

  • Obtaining Data to execute the test from the Database/CSV/API etc.
  • Navigating to a specific part of the WebApplication before starting the actual test
  • Restoring the WebApplication original status before starting the test (ie: If your test disables an user, it might be a good idea to enable it again in an @AfterMethod)


Creating your BaseTest

Start by creating new package in your project if you don’t already have it - right click on src/main/java, go to New -> Package and create: testrunner

Inside the package create a new abstract class (we don’t want anyone to create instances of the BaseTest!).
Copy the code below inside:

package testrunner;

import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;

import asserts.Validate;
import driver.DriverFactory;
import driver.DriverManager;
import listeners.TestMethodListener;
import logging.Logging;
import utils.Constants;

@Listeners(TestMethodListener.class)
public abstract class BaseTest implements Logging {
	
	protected static DriverManager driverManager;
	
	@BeforeMethod
	@Parameters({"driverName"})
	protected void setup(@Optional("CHROME") String driverName) {		
		initializeDriverManager(driverName);	
		driverManager.getDriver().navigate().to(Constants.getContextUrl());
	}
	
	/** Separating initialization of DriverManager because it's a static class and can have strange behaviors while running tests in parallel.
	 *  The synchronized Keyword is to prevent 2 Threads from calling a static class at the same time.
	 *  Read more about synchronization in <a>https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html</a>
	 */
	private synchronized void initializeDriverManager(String driverName) {
		if (null == driverManager) {
			driverManager = DriverFactory.valueOf(driverName).getDriverManager();
		} else {
			driverManager.getDriver();
		}
	}
	
	@AfterMethod
	protected void cleanUp() {
		driverManager.quitDriver();
	}
	
	protected WebDriver getDriver() {
		return driverManager.getDriver();
	}
}

The Method Constants.getContextUrl() contains the URL of your WebApplication, which will change depending on the Environment you’re executing against. To use that Method effectively you will need 2 more classes, and a property file.

Create a package utils in the root with the following Java code:

PropertyReader

package utils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import logging.Logging;

public class PropertyReader implements Logging {

    private Properties prop = new Properties();

    public PropertyReader() {   	
        try (InputStream in = getClass().getResourceAsStream("/" + Constants.PROPERTIES_NAME)){
            try {
            	getLogger().debug("Attempting .properties file load.");
                prop.load(in);
            } catch (FileNotFoundException e) {
            	getLogger().error(Constants.PROPERTIES_NAME + " Property file not found", e.getLocalizedMessage());
            } 
        
        } catch (IOException e) {
        	getLogger().error("Error reading file " + Constants.PROPERTIES_NAME, e.getLocalizedMessage());
        }
    }

    public String getString(String propertyName) {
        return prop.getProperty(propertyName);
    }
    
    public Integer getInt(String propertyName) {
    	int temp = -1;
    	
    	try {
		temp = Integer.parseInt(prop.getProperty(propertyName));
    	} catch (NumberFormatException e) {
    		getLogger().error("The property named: " + propertyName + " cannot be parsed to an Int.");
    	}
        return temp;
    }
}


Constants

package utils;

public class Constants {

	public static final String PROPERTIES_NAME = "blaze.properties"; // TODO Replace 'blaze' with your application name
	private static PropertyReader props = new PropertyReader();
	
	public static String getContextUrl() {
		return props.getString("url");
	}
}

Your .properties file, has to be located in the src/main/resources folder, and for now will contains a single line:
url=${url}
The ${url} will be replaced dynamically when your test starts, thanks to Maven Profiles !

Maven Profiles: How to run tests against different Environments

All you need is a small section in your pom.xml:

<profiles>
	<profile>
		<id>qa-blaze</id> <!-- Set to any name you want your profile to have -->
		<properties>
			<url>http://demoblaze.com/</url> <!-- This value is dynamically replaced in your properties file depending on your selected Profile. -->
		</properties>
		<activation>
			<activeByDefault>true</activeByDefault>
		</activation>
	</profile>
	<profile>
		<id>prod-blaze</id>
		<properties>
			<url>http://productionurl.com/</url> <!-- Set to the URL of your application. -->
		</properties>
	</profile>
</profiles>

This is very powerful, as you can run mvn clean install -P YOUR_PROFILE_NAME and use it to execute your tests against dev environment, QA, Staging, Production….

Read more about Maven Profiles:

Final Step: A Sample Test

Finally we are able to open a Browser. For every single Test and Page Object Classes, we will work in a different folder: src/test/java Because this is the default folder for your tests picked up by the maven-surefire-plugin. We can have tests in other folders/packages but it requires additional configuration and is not, generally speaking, a good practice.

Create a package inside that folder called tests, and in there a Java Class called SampleTest, something along these lines:

public class SampleTest extends BaseTest {

	@Test(description = "Sample test !")
	public void thisIsAnEmptyTest() throws Exception {
		Reporter.log("Starting Sample test");
	}
}

Noticed anything strange ? Even having an empty test, with 0 lines of code, I am able to:

  1. Execute the Test, which instantiates a WebDriver instance (Using Chrome by default - we can change that later)
  2. Navigate to the URL of the application, depending on the selected Environment.
  3. Once the test completes, BaseTest will cleanly and quietly close your WebDriver instance.

What about you, are you seeing the same results ? Feel free to drop me an e-mail if you want to debug unforseen problems.


Thank you for sticking with me, and see you in the next post.