Java, TestNG, Appium and More

Bakican Boydaş
17 min readSep 16, 2023

--

Photo was created from Dall-E

Hello, there are many automation documents and courses on the internet. I can say that this article will not be much different from these. However, BDD is generally used in these automation trainings. I will not use something like this for this article. I will show you how to create a framework from scratch using the POM design pattern. There are certain technologies we need here. I will mention these in the next heading. I hope this article will be a nice and understandable article for those who want to start automation or see something different.

Requirements

  1. Some Java knowledge
  2. Appium (Download link is here)
  3. An Android device
  4. IntelliJ (Download link is here)
  5. SDK tools (Download link is here)
  6. Maven (Download link is here)
  7. And Java (Download link is here)

Here we go

After making all the installations, we need to define the computer PATH. First of all, we save the file locations where the Java, Maven and SDK files are installed in the environment variables.

Afterwards, we save these additions in Path within the system variables.

After making our additions, we are done with this side by clicking OK and save. Now we need to check if our additions are working correctly. For this, we open the command system and check the versions respectively as shown in the picture below.

If we can see the versions correctly after adding all the environment variables, there is no problem.

Now let’s open IntelliJ and create our first POM design pattern here.

The things we need to pay attention to when creating a project with IntelliJ are that the language we will use in the project must be Java and the building system must be selected Maven. I am using Java version 11 in this project, you can use this version or a different version if you wish. It is worth mentioning that if the version is different, some code pieces may need to be written differently. After our project is opened, let’s start creating our POM pattern.

POM Design Pattern

The POM (Page Object Model) is a design pattern used in software testing and test automation to enhance the maintainability and reusability of test scripts and improve the overall efficiency of test automation efforts, especially in web applications. It is not specific to a particular programming language or testing framework but is a concept that can be applied in various testing environments.

This definition above is a definition created by Chat GPT. And itexplained very well what the POM design pattern means in short. In our project, I will show the file directories required for this structure in the picture below, and I think you will not have any difficulties in the rest of the article if you use this file arrangement and naming in your project.

We delete the Main directory from our project because we have no business with this part. The simpler the directories look, the easier it is to track projects.

Now here we have a home directory under the src directory. This is the test directory. We have 2 different directories under the test directory. These are java and resources directories. There are 3 different packages under the java directory: “base”, “Framework”, “utils” respectively. There are 3 different properties files under the resources directory: “config.properties”, “extent.properties”, “log4j2.properties” respectively. I will explain what these properties files do later. Before these, let’s create the packages under the “Framework” and “utils” packages, respectively. While there are “pageObjects” and “pageTests” packages under the Framework package, there are “extentReports”, “listeners” and “logs” packages under the “utils” package. And finally we have the testngSuites directory located in the same location as the src directory. And we have the pom.xml file, which is automatically created when the project is created. I know it may seem complicated for someone encountering such a system for the first time, but I hope it will all be understandable for you at the end of the article.

Now, first of all, we need to specify the technologies we want to use and which versions we want to use in pom.xml.

pom.xml

The pom.xml file is the file that allows us to use the technologies we want to use with Maven. The technologies we want to use here are specified as <dependency> technology information to be used </dependency> between <dependencies> </dependencies> tags.

Below I will show you how to write these and the versions I use. However, if you want to use different versions or different technologies, you can access the versions you want from this site https://mvnrepository.com/ .If you want to follow what is done in this article, I recommend you use the versions I used.

If you want to use it the way I use it, you can use it by copy-pasting the code block I will leave below. Afterwards, all you have to do is reload Maven.

   <dependencies>

<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>7.6.0</version>
</dependency>

<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.7.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>

<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>5.1.0</version>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>provided</scope>
</dependency>

</dependencies>

Now we are one step closer to completing our project.

What is what?

Now, let’s see what the final version will be like when we create our project with its classes. The reason I show you directly here is to explain how our project will work.

First, let’s talk about what Base Page and Base Test classes are. Our Base Page class is the class that extends the pages we will create. This class contains common functions that we will use throughout the project. For example, to click a button;

public void clickToElement(WebElement key){
key.click();
}

We specify the command in this class. Such general-use functions are located in our Base Page class.

Our Base Test class is the class that defines the necessary parameters and capabilities for testing our test classes with the Appium driver and enables the test to be started and terminated. So, if we say Base Page is the heart, we can say that the Base Test class is the brain for us.

The pages to be tested are written separately in the pageObjects package within the “Framework” package. For example, if an application has a homepage and user page, these are specified in different classes. In this way, the elements we find for the pages and the operations to be performed on the pages are written separately, making code tracking easier. This is one of the best uses of the POM design pattern. On the pageTests side, if the page to be tested is the homepage, the test steps of the test to be performed on that page are written and our tests are carried out in these classes.

There are 3 packages located under the utils package. These are extentReport, listeners and logs packages. In fact, the classes we use here are not necessary for our test to run. But every test must have a result, and this result must be easily observable and analysable. TestNG has its own reporting functions, but they are not very useful. For this reason, I wanted to use extenReport and log as a 3rd party software. In this way, we can make our test reports more understandable and analysable.I will not go into detail about the content of the classes here later in the article. I will only describe the necessary sides because different research can be done about this side from different sources. Here, while the extent report allows us to report beautifully, we need to have a class that will give this information to the extent report. We use the TestListener class, which indicates whether the test has started, finished, failed or passed. And if the test fails, we use the retry and annotationtransformer classes, which allow us to test again. In fact, they all get their test information from the TestListener class. Our Log class is the class we use to keep the logging that we know is always used. I will explain the necessary information about the utils package and its contents in the later part of the article, apart from that, I will give you resources that you can follow for the remaining parts.

In our Resources package, we ensure that the information we need for testing is used from this side. For example, in our config.properties file, if we need a user email or password during testing, we get it from this step. In this way, it allows us to easily test different users. The exten.properties file contains the information that the extent report should use when creating it. Likewise, the log4j2.properties file contains the information that determines the format of our log file.

And finally, our testng.xml file is the file where the parameters used in our tests are determined and which test classes will be tested. We can call Base Page the heart, Base Test the brain, and testng.xml the vein. The blood required for our test is transported thanks to this file.

I know this part is a bit long, but it can be a little difficult to explain why we use the POM design pattern and how to use it. I chose this method of explanation because I thought it would be more understandable to explain all directories and packages one by one. Now that the boring part is over, we can move on to the coding part.

Coding

First of all, I want to start this section with Base Test. Because we cannot live without the brain.

Base Test

We can say that the Base Test class consists of 3 parts. These can be divided into 3 groups: creating objects, creating the driver and giving the capabilities, and reading the BeforeMethod and property file.

public class BaseTest {

public AppiumDriver driver;

//PAGES
public HomePageObjects homePageObjects;
public LoginPageObjects loginPageObjects;


public Properties properties;
FileInputStream fileInputStream;
public DesiredCapabilities capabilities;


public AppiumDriver getDriver(){return driver;}

In this part, we create our objects.

@Parameters({"platformName","udid","appPackage","appActivity","implicityWait"})
@BeforeClass(alwaysRun = true)
public void setup(String platformName,String udid, String appPackage, String appActivity, String implicityWait) throws Exception{
capabilities = new DesiredCapabilities();

if(platformName.equals("Android")){
capabilities.setCapability("platformName","Android");
capabilities.setCapability("udid",udid);
capabilities.setCapability("appPackage",appPackage);
capabilities.setCapability("appActivity",appActivity);
}
else if(platformName.equals("IOS")){
capabilities.setCapability("platformName","IOS");
capabilities.setCapability("udid",udid);
capabilities.setCapability("appPackage",appPackage);
capabilities.setCapability("appActivity",appActivity);
}
try {
URL url = new URL("http://0.0.0.0:4723/wd/hub");
Log.info("Test başlıyor!");
driver = new AppiumDriver<MobileElement>(url,capabilities);
//driver = new AndroidDriver<MobileElement>(url,capabilities);
//driver = new IOSDriver<MobileElement>(url,capabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}
int impWait = Integer.parseInt(implicityWait);
driver.manage().timeouts().implicitlyWait(impWait, TimeUnit.SECONDS);
//return getDriver();
}

In this section, we specify our capability values and url values required for the Appium driver to work. And we specify an implicity time for our driver.

@BeforeMethod(alwaysRun = true)
public void methodSetup() throws IOException {
getReadPropFile();
homePageObjects = new HomePageObjects(driver);
loginPageObjects = new LoginPageObjects(driver);
}

public void getReadPropFile() throws IOException {
properties = new Properties();
fileInputStream = new FileInputStream("src/test/resources/config.properties");
properties.load(fileInputStream);
}

@AfterClass(alwaysRun = true)
public void teardown() {
Log.info("Test bitti.");
driver.quit();}
}

Finally, before the test starts, we add the pages to be tested, the properties file from which the information will be obtained, and the sections that allow the driver to be stopped after the test is completed. And in this way, our code written for Base Test is exactly as follows.

public class BaseTest {

public AppiumDriver driver;

//PAGES
public HomePageObjects homePageObjects;
public LoginPageObjects loginPageObjects;


public Properties properties;
FileInputStream fileInputStream;
public DesiredCapabilities capabilities;


public AppiumDriver getDriver(){return driver;}
@Parameters({"platformName","udid","appPackage","appActivity","implicityWait"})
@BeforeClass(alwaysRun = true)
public void setup(String platformName,String udid, String appPackage, String appActivity, String implicityWait) throws Exception{
capabilities = new DesiredCapabilities();

if(platformName.equals("Android")){
capabilities.setCapability("platformName","Android");
capabilities.setCapability("udid",udid);
capabilities.setCapability("appPackage",appPackage);
capabilities.setCapability("appActivity",appActivity);
}
else if(platformName.equals("IOS")){
capabilities.setCapability("platformName","IOS");
capabilities.setCapability("udid",udid);
capabilities.setCapability("appPackage",appPackage);
capabilities.setCapability("appActivity",appActivity);
}
try {
URL url = new URL("http://0.0.0.0:4723/wd/hub");
Log.info("Test başlıyor!");
driver = new AppiumDriver<MobileElement>(url,capabilities);
//driver = new AndroidDriver<MobileElement>(url,capabilities);
//driver = new IOSDriver<MobileElement>(url,capabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}
int impWait = Integer.parseInt(implicityWait);
driver.manage().timeouts().implicitlyWait(impWait, TimeUnit.SECONDS);
//return getDriver();
}

@BeforeMethod(alwaysRun = true)
public void methodSetup() throws IOException {
getReadPropFile();
homePageObjects = new HomePageObjects(driver);
loginPageObjects = new LoginPageObjects(driver);
}

public void getReadPropFile() throws IOException {
properties = new Properties();
fileInputStream = new FileInputStream("src/test/resources/config.properties");
properties.load(fileInputStream);
}

@AfterClass(alwaysRun = true)
public void teardown() {
Log.info("Test bitti.");
driver.quit();}
}

Base Page

Now, we do not need to divide it into parts as in the Base Test class. Here the class consists of 2 parts. Creating the driver we will use and entering its information and the functions to be used throughout the test. I will send this class directly below with all its codes. There is nothing to explain here, the functions listed below may vary from project to project. Most of the functions written here will not be used for this project. There are functions written purely for example purposes, you can change or remove these functions as you wish and as needed. Likewise, you can add your own functions.

public class BasePage {
AppiumDriver driver;
WebDriverWait wait;
Actions action;

public BasePage(AppiumDriver driver){
this.driver = driver;
this.wait = new WebDriverWait(driver, 10);
this.action = new Actions(driver);

}

public WebElement findElement(By key){
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(key));
return element;
}


public void clickToElement(By key){
findElement(key).click();
waitForSecond(2);
}
public void clickToElement(WebElement key){
key.click();
}

public void sendKeysToElement(By key,String text){
findElement(key).sendKeys(text);
}

public boolean checkElementIfVisibleTrue(By key){
wait.until(ExpectedConditions.presenceOfElementLocated(key));
return true;
}

public void checkElementIfExist(By key){
if(findElement(key) != null){
Assert.assertTrue(true,"Element is found");
}
else if(findElement(key) == null){
Assert.assertFalse(true,"Element cannot find");
}
}

public void scroolDownUntilFindTheElement(By key){

int maxRetryCount = 10;
while (maxRetryCount > 0) {
try {
if (checkElementIfVisibleTrue(key)) {
break;
}
}catch (Exception e){
maxRetryCount--;
new TouchAction(driver)
.press(PointOption.point(506, 1829))
.waitAction(waitOptions(ofMillis(1000)))
.moveTo(PointOption.point(506, 1285))
.release()
.perform();
if (maxRetryCount == 0) {
Assert.assertFalse(false,"Element cannot find");
break;
}
}
}

}

public void scroolDownUntilFindTheElement(WebElement key){

int maxRetryCount = 10;
while (maxRetryCount > 0) {
try {
if (key.isEnabled()) {
break;
}
}catch (Exception e){
maxRetryCount--;
new TouchAction(driver)
.press(PointOption.point(506, 1829))
.waitAction(waitOptions(ofMillis(1000)))
.moveTo(PointOption.point(506, 1285))
.release()
.perform();
if (maxRetryCount == 0) {
Assert.assertFalse(false,"Element cannot find");
break;
}
}
}

}



public void swipeToLeftNTime(Integer swipeCount){

int maxRetryCount = swipeCount;
while (maxRetryCount > 0) {
maxRetryCount--;
new TouchAction(driver)
.press(PointOption.point(850, 1100))
.waitAction(waitOptions(ofMillis(1000)))
.moveTo(PointOption.point(330, 1100))
.release()
.perform();
if (maxRetryCount == 0) {
System.out.println("Swipe is over.");
break;
}

}

}

public void waitForSecond(int seconds) {
try {
Thread.sleep(seconds*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void clickElementIfExists(By key) {
try{
if(checkElementIfVisibleTrue(key)){
clickToElement(key);
}
}catch (Exception e){
System.out.println("Element bulunamadı");
}
}

public void deviceGoBack(){
driver.navigate().back();
}

public void emptyTab(){
action.click();
}

protected String getInfoMessage(String message) {
ExtentTestManager.getTest().log(Status.INFO, message);
return message;
}
}

HomePageObjects and LoginPageObjects

We will discuss the classes mentioned here together because the formats of the pages will always be the same. In these classes, you can create different classes for different applications you will test and increase the number of pages to be tested. These classes extend from the BasePage class, and the functions used within the functions are used by BasePage. These classes always consist of 3 sections. These are specifying the driver to be used, specifying the elements and specifying the functions to be used for testing.

public class HomePageObjects extends BasePage {

public HomePageObjects(AppiumDriver driver) {super(driver);}

By userIcon = By.id("com.turknet.oim:id/tv_user_short_cut");

public HomePageObjects checkHomePage(){
checkElementIfExist(userIcon);
return this;
}
}
public class LoginPageObjects extends BasePage {

public LoginPageObjects(AppiumDriver driver) {
super(driver);
}

By tcField = By.id("com.turknet.oim:id/et_login_tckn");
By passwordField = By.id("com.turknet.oim:id/et_login_password");
By loginButton = By.id("com.turknet.oim:id/btn_login");

public LoginPageObjects checkTCField(){
checkElementIfExist(tcField);
return this;
}

public LoginPageObjects checkPasswordField(){
checkElementIfExist(passwordField);
return this;
}

public LoginPageObjects loginToAccount(String tcNo, String password){
sendKeysToElement(tcField,tcNo);
sendKeysToElement(passwordField,password);
clickElementIfExists(loginButton);
return this;

}
}

HomeTest

First of all, I need to explain what will be tested here. After logging in to the mobile application called Türknet, the user accesses the home page. And if the user can see the icon on the home page, it detects that he has accessed the home page and passes the test as a pass.

Here the user enters his/her ID number and password, clicks the continue button and logs in.

If the icon shown with a red arrow appears on the home page after logging in, you have logged in successfully and reached the home page. Test pass is given. Now let’s see how this is accomplished within the HomeTest class.

public class HomeTest extends BaseTest {
@BeforeMethod
public void loginToAccount(Method method){
startTest(method.getName(),"Ana sayfanın açılması ve kontrolü");
loginPageObjects.loginToAccount(properties.getProperty("tcNo"),properties.getProperty("password"));


}

@Test(priority = 0, description = "Check home page")
public void checkHomePage(){
homePageObjects.checkHomePage();

}


}

Here, in the BeforeMethod section, the user gets the required ID number and password from config.properties and logs in with the help of the loginToAccount function in loginPageObjects. You can see how the loginToAccount function works in the LoginPageObjects we mentioned above. After the user logs in, the checkHomePage function used in homePageObjects checks whether the user icon exists. If there is, our test is passed. Again, you can see how the checkHomePage function works from the codes we specified in HomePageObjects.

Another important issue here is the startTest function. Thanks to this function, ExtentTestManager learns that the test has started, and TestListener can follow the steps of the test with the getTest function in ExtentTestManager and use it for both Retry and ExtentReport. I will not explain the codes here in detail, I just explained why we used the startTest method. Those who want to examine these codes in more detail can look at this page https://www.swtestacademy.com/extent-reports-in-selenium-with-testng/

All Utils

I will not explain the codes in detail here because this part is a different part that is far from our topic. I have given the necessary link for this section in the line above, you can see the rest here, but I will give the codes in this section so that the reporting of the project works correctly. You can copy and paste these codes.

ExtentManager

public class ExtentManager {
public static final ExtentReports extentReports = new ExtentReports();
public synchronized static ExtentReports createExtentReports() {
ExtentSparkReporter reporter = new ExtentSparkReporter("./extent-reports/extent-report.html");
reporter.config().setReportName("Turknet Extent Report");
extentReports.attachReporter(reporter);
extentReports.setSystemInfo("Turknet", "Mobile Test");
extentReports.setSystemInfo("Author", "Bakican Boydas");
return extentReports;
}
}

ExtentTestManager

public class ExtentTestManager {
static Map<Integer, ExtentTest> extentTestMap = new HashMap<>();
static ExtentReports extent = ExtentManager.createExtentReports();
public static synchronized ExtentTest getTest() {
return extentTestMap.get((int) Thread.currentThread().getId());
}
public static synchronized ExtentTest startTest(String testName, String desc) {
ExtentTest test = extent.createTest(testName, desc);
extentTestMap.put((int) Thread.currentThread().getId(), test);
return test;
}
}

AnnotationTransformer

public class AnnotationTransformer implements IAnnotationTransformer {
@Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
annotation.setRetryAnalyzer(Retry.class);
}
}

Retry

public class Retry implements IRetryAnalyzer {
private int count = 0;
private static int maxTry = 1; //Run the failed test 2 times

@Override
public boolean retry(ITestResult iTestResult) {
if (!iTestResult.isSuccess()) { //Check if test not succeed
if (count < maxTry) { //Check if maxTry count is reached
count++; //Increase the maxTry count by 1
iTestResult.setStatus(ITestResult.FAILURE); //Mark test as failed and take base64Screenshot
extendReportsFailOperations(iTestResult); //ExtentReports fail operations
return true; //Tells TestNG to re-run the test
}
} else {
iTestResult.setStatus(ITestResult.SUCCESS); //If test passes, TestNG marks it as passed
}
return false;
}

public void extendReportsFailOperations(ITestResult iTestResult) {
Object testClass = iTestResult.getInstance();
WebDriver webDriver = ((BaseTest) testClass).getDriver();
String base64Screenshot = "data:image/png;base64," + ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.BASE64);
getTest().log(Status.FAIL, "Test Failed",
getTest().addScreenCaptureFromBase64String(base64Screenshot).getModel().getMedia().get(0));
}
}

TestListener

public class TestListener extends BaseTest implements ITestListener {
private static String getTestMethodName(ITestResult iTestResult) {
return iTestResult.getMethod().getConstructorOrMethod().getName();
}

@Override
public void onStart(ITestContext iTestContext) {
Log.info("I am in onStart method " + iTestContext.getName());
iTestContext.setAttribute("AppiumDriver", this.driver);
}

@Override
public void onFinish(ITestContext iTestContext) {
Log.info("I am in onFinish method " + iTestContext.getName());
//Do tier down operations for ExtentReports reporting!
ExtentManager.extentReports.flush();
}

@Override
public void onTestStart(ITestResult iTestResult) {
Log.info(getTestMethodName(iTestResult) + " test is starting.");
}

@Override
public void onTestSuccess(ITestResult iTestResult) {
Log.info(getTestMethodName(iTestResult) + " test is succeed.");
//ExtentReports log operation for passed tests.
getTest().log(Status.PASS, "Test passed");
}

@Override
public void onTestFailure(ITestResult iTestResult) {
Log.info(getTestMethodName(iTestResult) + " test is failed.");

//Get driver from BaseTest and assign to local webdriver variable.
Object testClass = iTestResult.getInstance();
WebDriver driver = ((BaseTest) testClass).getDriver();

//Take base64Screenshot screenshot for extent reports
String base64Screenshot =
"data:image/png;base64," + ((TakesScreenshot) Objects.requireNonNull(driver)).getScreenshotAs(OutputType.BASE64);

//ExtentReports log and screenshot operations for failed tests.
getTest().log(Status.FAIL, "Test Failed",
getTest().addScreenCaptureFromBase64String(base64Screenshot).getModel().getMedia().get(0));
}

@Override
public void onTestSkipped(ITestResult iTestResult) {
Log.info(getTestMethodName(iTestResult) + " test is skipped.");
//ExtentReports log operation for skipped tests.
getTest().log(Status.SKIP, "Test Skipped");
}

@Override
public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) {
Log.info("Test failed but it is in defined success ratio " + getTestMethodName(iTestResult));
}

Log

public class Log {
//Initialize Log4j instance
private static final Logger Log = LogManager.getLogger(Log.class);

//Info Level Logs
public static void info (String message) {
Log.info(message);
}

//Warn Level Logs
public static void warn (String message) {
Log.warn(message);}
//Error Level Logs
public static void error (String message) {
Log.error(message);
}

//Fatal Level Logs
public static void fatal (String message) {
Log.fatal(message);
}

//Debug Level Logs
public static void debug (String message) {
Log.debug(message);
}
}

Resources

The only file we need here is the config.properties file. Other files are the files that specify how the reporting formats will be. The config file is the file where the information we need during the test is written. The config file will be as follows. You can add any extra information you want here when you will use it in your test.

tcNo = ID number will be written here
password = The password will be written here too.

extent.properties

extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html

log4j2.properties

#Declare loggers
status = error
name= Log4j2PropertiesConfig
appenders=a_console, a_rolling
rootLogger.level=info
rootLogger.appenderRefs = ar_console,ar_rolling
rootLogger.appenderRef.ar_console.ref = StdoutAppender
rootLogger.appenderRef.ar_rolling.ref= RollingAppender

#Console Logger
appender.a_console.type = Console
appender.a_console.name = StdoutAppender
appender.a_console.layout.type = PatternLayout
appender.a_console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n

#Rolling Logger
appender.a_rolling.type = RollingFile
appender.a_rolling.name = RollingAppender
appender.a_rolling.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
appender.a_rolling.fileName=log4j2/log4j2-test-automation.log
appender.a_rolling.filePattern=log4j2-sample-%d{yyyy-MM-dd}.log
appender.a_rolling.layout.type = PatternLayout
appender.a_rolling.policies.type = Policies
appender.a_rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.a_rolling.policies.time.interval = 1

# To change log file every day
appender.a_rolling.policies.time.modulate = true
# To change log file after 10MB size
appender.a_rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.a_rolling.policies.size.size=10MB
appender.a_rolling.strategy.type = DefaultRolloverStrategy
appender.a_rolling.strategy.max = 20

testng.xml

And finally, it’s time to create our testng.xml file where we will run our project. This file consists of 3 parts. These are listeners, parameter and class sections. In the Listener section, we specify the listener files we will use for our test. In the parameter section, we specify the device information and package information, and in the class section, we specify which test class we will test. We have a single test class for this project, but if we had more test classes, we could test them as a package. xml file is as follows.

<suite name="Turknet Test Suite">

<listeners>
<listener class-name="utils.listeners.TestListener"/>
<listener class-name="utils.listeners.AnnotationTransformer"/>
</listeners>
<test name="Turknet Tests">
<parameter name="platformName" value="Android"></parameter>
<parameter name="udid" value="R5CT62X6DNM"></parameter>
<parameter name="appPackage" value="com.turknet.oim"></parameter>
<parameter name="appActivity" value="com.turknet.oim.features.loginotp.presentation.login.LoginActivity"></parameter>
<parameter name="implicityWait" value="10"></parameter>
<classes>
<class name="Framework.pageTests.HomeTest"></class>
</classes>
</test>
</suite>

Start the test and view results

We right-click on the testng.xml file and start our test as shown.

After the test is completed, you can see the result as in the image below. After the test is completed, two different files named extent-reports and log4j2 will be created and you will be able to view the test report in these files.

--

--