qa docs.

QA Automation

Master building robust frameworks from scratch.

1

Introduction to the Stack

Maven (Builder), Selenium (Driver), TestNG (Brain).

2

Manual Project Setup

Create the strict Maven directory structure.

3

The POM.xml

Pull down required automation dependencies.

4

Writing the Java Code

Write your first script using @BeforeMethod & @Test.

5

Compiling and Running

Execute via terminal and understand reports.

6

Q&A: mvn test vs clean test

Understanding Maven lifecycles and the target cache.

7

Page Object Model (POM)

Organizing code to represent pages instead of messy scripts.

8

Step-by-Step E2E Test

Write a complete end-to-end shopping cart checkout flow.

1. Introduction to the Stack

Building an automation framework from scratch is the best way to deeply understand how the tools interact. We will use three primary tools:

Maven

The builder. It handles downloading all external libraries automatically so you don't have to manually manage .jar files.

Selenium

The driver. It provides the commands (like click, type, find) to physically interact with the web browser.

TestNG

The brain. It organizes the code into tests, provides assertions (Pass/Fail), and generates reports.

2. Manual Project Setup

To use Maven, we must adhere to its strict folder structure. This separates application source code from testing source code.

Open your terminal in an empty folder (e.g., qa-framework) and run the following commands to build the skeleton:

mkdir -p src/main/java
mkdir -p src/test/java

Why this structure?

  • src/main/java is where developers put the actual app code (we will ignore this, since we are only writing tests).
  • src/test/java is where QA engineers put all automation scripts.

3. The POM.xml

For Maven to know what to download, it looks for a file named pom.xml (Project Object Model) in the root of your folder.

Create a file named pom.xml in your root folder, and paste this in:

<?xml version="1.0" encoding="UTF-8"?>
<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.qastore</groupId>
    <artifactId>automation-tests</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- 1. Selenium -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.23.0</version>
        </dependency>

        <!-- 2. TestNG -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.10.2</version>
            <scope>test</scope>
        </dependency>

        <!-- 3. WebDriverManager -->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>5.9.1</version>
        </dependency>
    </dependencies>
</project>

Deep Understanding

We include WebDriverManager because otherwise, you would have to manually download a chromedriver.exe file every time Google Chrome updates. It manages the binary compatibility for you seamlessly!

4. Writing the Java Code

Now, let's write the actual Java code. In src/test/java, create a file named BasicTest.java:

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class BasicTest {
    WebDriver driver;

    // @BeforeMethod runs automatically BEFORE every single @Test
    @BeforeMethod
    public void setup() {
        WebDriverManager.chromedriver().setup(); 
        driver = new ChromeDriver();             
    }

    // @Test is the actual script. TestNG looks for this tag to execute it.
    @Test
    public void myFirstTest() {
        // Selenium command
        driver.get("https://qa.randomly.online/");
        String pageTitle = driver.getTitle();
        
        // TestNG command
        Assert.assertTrue(pageTitle.contains("QA Store"), "The title did not match!");
    }

    // @AfterMethod runs automatically AFTER every single @Test, even if it fails
    @AfterMethod
    public void teardown() {
        if(driver != null) {
            driver.quit(); 
        }
    }
}

Deep Dive on Annotations

Notice how we separate logic using Annotations (@BeforeMethod, @Test, @AfterMethod). If a test fails halfway through, normal Java code would just crash and leave a ghost Chrome window open forever in your RAM. By using TestNG's @AfterMethod, we guarantee that driver.quit() is executed no matter what happens.

5. Compiling and Running

Now that you have your code, open your terminal in the root folder (where pom.xml is) and run:

mvn test

What happens when you press enter?

1

Maven reads your pom.xml.

2

It downloads dependencies from the internet (takes a minute on the first run).

3

It compiles BasicTest.java into computer-readable bytecode.

4

It tells TestNG to look for any method with a @Test annotation.

5

TestNG runs @BeforeMethod, then @Test, then @AfterMethod.

6

TestNG prints a summary showing Pass, Fail, or Skip.

6. Q&A: mvn test vs clean test

When executing your automation scripts, you will frequently use Maven commands in the terminal. The two most common are mvn test and mvn clean test. Here is the deep technical difference between them.

The target/ Directory

Before understanding the commands, you must understand the target/ directory. When Maven compiles your Java code (from .java to computer-readable .class files), it places them in a hidden folder named target/.

# The target folder structure
qa-framework/
├── pom.xml
├── src/
└── target/                  <-- Maven generates this automatically
    ├── classes/             <-- Compiled app code
    ├── test-classes/        <-- Compiled QA scripts (BasicTest.class)
    └── surefire-reports/    <-- Test results (HTML/XML)

Command 1: mvn test

This command tells Maven to compile your code and run the tests. However, it is "lazy" to save time.

  • It looks at your .java files and compares them to the .class files in target/.
  • If it thinks the file hasn't changed, it skips compilation and uses the old version.
  • The Danger: Sometimes Maven gets confused. You might fix a bug in your code, run mvn test, and it still fails because Maven used the old cached version!

Command 2: mvn clean test

This chains two Maven phases together: clean and then test.

  • Phase 1 (clean): It physically deletes the entire target/ folder. All old compiled code and reports are destroyed.
  • Phase 2 (test): It rebuilds the target/ folder completely from scratch and runs the tests.
  • The Benefit: It guarantees you are running the absolute latest version of your code. You never have to worry about "ghosts" in the cache.

The Golden Rule

Always use mvn clean test if you are running into unexplainable bugs, or right before you push your code to GitHub. It takes a few extra seconds, but guarantees a fresh execution!

7. Page Object Model (POM)

In the real world, tests get huge. If you put all your button clicks inside your test script, it becomes impossible to read and maintain. Instead, we use the Page Object Model.

The Rule: For every physical page in your web app, you create one Java class to represent it.

Structure

Inside src/test/java/com/qastore, create a folder named pages. This keeps your test scripts separated from your page blueprints.

package com.qastore.pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage {
    private WebDriver driver;

    // 1. Locators (How to find elements on the page)
    private By usernameInput = By.cssSelector("input[type='email']");
    private By passwordInput = By.cssSelector("input[type='password']");
    private By loginButton = By.xpath("//button[text()='Sign In']");

    // 2. Constructor (Hooking up the driver)
    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    // 3. Actions (What can a user do on this page?)
    public void enterUsername(String username) {
        driver.findElement(usernameInput).sendKeys(username);
    }

    public void enterPassword(String password) {
        driver.findElement(passwordInput).sendKeys(password);
    }

    public void clickLogin() {
        driver.findElement(loginButton).click();
    }
}

Why do this?

If the developers change the login button from Sign In to Login Now, you only have to update one line in the LoginPage.java class, and all 50 tests that use it will instantly be fixed!

8. Step-by-Step E2E Test

Let's write a complete End-to-End (E2E) test. This simulates a real user going to the store, logging in, and checking out.

The Test Script

Create a file named CheckoutE2ETest.java inside src/test/java/com/qastore/.

package com.qastore;

import com.qastore.pages.LoginPage;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class CheckoutE2ETest {
    private WebDriver driver;
    private LoginPage loginPage;

    @BeforeMethod
    public void setup() {
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        
        // Initialize the page objects
        loginPage = new LoginPage(driver);
    }

    @Test
    public void testFullCheckoutFlow() throws InterruptedException {
        // Step 1: Go to the Login Page
        driver.get("https://qa.randomly.online/login");

        // Step 2: Use the Page Object to interact
        loginPage.enterUsername("test@example.com");
        loginPage.enterPassword("password123");
        loginPage.clickLogin();

        // Pause so we can visually see the login happen (Not recommended for real frameworks!)
        Thread.sleep(3000); 

        // In a complete framework, you would now have a HomePage object to click "Add to Cart",
        // and a CartPage object to click "Checkout".
        System.out.println("E2E Test executed successfully!");
    }

    @AfterMethod
    public void teardown() {
        if(driver != null) {
            driver.quit();
        }
    }
}

You're an Automation Engineer!

You've officially created a professional Maven architecture, implemented the Page Object Model, and written an E2E test. Next time you run mvn test, watch the magic unfold!