Overview:
I have been running thousands of automated regression test cases in multiple test environments for years. As part of CI / CD pipeline, I run the tests in QA, UAT, Staging and PROD. In this article, I would like to show you the approach I follow to make the tests run on any given test environment.
Many of us tend to write the scripts as shown below with a lot of variables and if-else blocks which is very difficult to maintain.
if(environment.equals("prod")){
url="http://testautomationguru.com";
username="tag";
password="password";
}else if(environment.equals("qa")){
url="http://qa.testautomationguru.com";
username="qatag";
password="qapassword";
}else if(environment.equals("dev")){
url="http://dev.testautomationguru.com";
username="devtag";
password="devpassword";
}
driver.get(url);
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
Now adding few environment details, say DB connections details to this script is not an easy task! It requires a lot of code change and testing to ensure that the script is not broken.
Keeping Environment Specific Data:
In order to make the script work for any given environment, We should avoid using any hard coded environment specific details in the script. As part of this article, Lets assume, We would need below details to run the script.
- application URL
- admin username
- admin password
- database host / IP address
- database port
- database username
- database password
I would suggest you to keep the environment specific details completely away from the test data as It is not going to change for each test. So, Lets create a property file as shown here.
# application properties
url=http://testautomationguru.com
username=tag
password=password123
# databsse properties
db.hostname=db.testautomationguru.com
db.port=3306
db.username=dba_admin
db.password=secured!
Lets maintain a separate property file for each environment as shown here.
Accessing Environment Specific Data:
To access environment specific data, I will be using Java – Owner library.
- Add the below dependency in your Maven project.
<dependency>
<groupId>org.aeonbits.owner</groupId>
<artifactId>owner</artifactId>
<version>1.0.8</version>
</dependency>
- Create an Interface as shown here.
@Sources({
"classpath:qa.properties" // mention the property file name
})
public interface Environment extends Config {
String url();
String username();
String password();
@Key("db.hostname")
String getDBHostname();
@Key("db.port")
int getDBPort();
@Key("db.username")
String getDBUsername();
@Key("db.password")
String getDBPassword();
}
- If the name of the method matches with the key of the property file, we can just call the method to access the value. (For ex: the method url() will refer to the key url in the property file)
- If the name of the method does not match with key in the property file, then we need to use @Key explicitly. (For ex: getDBPassword() will fetch the value of db.password by using @Key in the above Interface.)
- By using ConfigFactory, we create an instance of the Environment interface & access the property file.
Environment testEnvironment = ConfigFactory.create(Environment.class);
// prints http://qa.testautomationguru.com
System.out.println(testEnvironment.url());
// prints qa.db.testautomationguru.com
System.out.println(testEnvironment.getDBHostname());
- Now throughout your test and page objects, you would be using the testEnvironment object.
public class EnvironmentTest {
Environment testEnvironment;
@Test
public void functionalTest() {
System.out.println(testEnvironment.url());
System.out.println(testEnvironment.getDBHostname());
System.out.println(testEnvironment.getDBPassword());
}
@BeforeTest
public void beforeTest() {
testEnvironment = ConfigFactory.create(Environment.class);
}
}
- There is a small issue with the above approach that the environment is hard coded to read the qa.properties file. We could easily fix this by using a variable as shown here.
@Sources({
"classpath:${env}.properties"
})
public interface Environment extends Config {
String url();
String username();
String password();
@Key("db.hostname")
String getDBHostname();
@Key("db.port")
int getDBPort();
@Key("db.username")
String getDBUsername();
@Key("db.password")
String getDBPassword();
}
- Now we could pass the environment name as a variable to the test as shown here.
<suite name="TAG Suite">
<parameter name="environment" value="prod"/>
<test name="Simple example">
<-- ... -->
- TestNG test should access the environment parameter from the suite.xml and use it to read the specific property file.
public class EnvironmentTest {
Environment testEnvironment;
@Test
public void functionalTest() {
System.out.println(testEnvironment.url());
System.out.println(testEnvironment.getDBHostname());
System.out.println(testEnvironment.getDBPassword());
}
@BeforeTest
@Parameters({"environment"})
public void beforeTest(String environemnt) {
ConfigFactory.setProperty("env", environemnt);
testEnvironment = ConfigFactory.create(Environment.class);
}
}
Summary:
Adding any new environment specific data is very easy with this approach. Your tests and page objects remain unchanged. We need to update only Environment interface.
You can run the test against any given environment Dev, QA, UAT, Pre-Production, PROD without any code change. Name of the environment can be passed to the test as a parameter as shown above.
Happy Testing & Subscribe 🙂