☕ Java Fundamentals

Complete Programming Guide for QA/SDET Engineers

Part 3 of 4

🏭️ Classes & Objects

Object-Oriented Programming (OOP) organizes code into objects that bundle data and methods together. This is the foundation of test automation frameworks like Page Object Model.

🎯 OOP in QA Automation:
  • Page Object Model: Each web page is a class with elements and methods
  • Test Data Objects: Classes to represent test data structures
  • Reusability: Create base classes for common test operations
  • Maintainability: Changes in one class don't affect others

What is a Class?

A class is a blueprint or template for creating objects. It defines properties (variables) and behaviors (methods). Think of it as a cookie cutter - the class is the cutter, objects are the cookies.

Key Components:

  • Instance Variables: Data/properties of the object
  • Constructor: Special method to initialize objects
  • Methods: Actions/behaviors the object can perform
  • this keyword: Refers to the current object instance
// Class definition
class TestCase {
    // Instance variables (properties/attributes)
    String name;
    String status;
    int executionTime;
    
    // Constructor - called when creating object
    public TestCase(String name) {
        this.name = name;
        this.status = "Not Run";
        this.executionTime = 0;
    }
    
    // Methods (behaviors/functions)
    public void execute() {
        System.out.println("Executing: " + name);
        this.status = "Running";
    }
    
    public void pass() {
        this.status = "Passed";
        System.out.println(name + ": ✓ PASSED");
    }
    
    public void fail() {
        this.status = "Failed";
        System.out.println(name + ": ✗ FAILED");
    }
}

public class Main {
    public static void main(String[] args) {
        // Create objects (instances of the class)
        TestCase test1 = new TestCase("Login Test");
        TestCase test2 = new TestCase("Logout Test");
        
        // Use object methods
        test1.execute();
        test1.pass();
        
        test2.execute();
        test2.fail();
    }
}

Constructors

Constructors are special methods called when creating objects. They initialize the object's state.

class Browser {
    String name;
    String version;
    boolean headless;
    
    // Default constructor (no parameters)
    public Browser() {
        this.name = "Chrome";
        this.version = "latest";
        this.headless = false;
    }
    
    // Parameterized constructor
    public Browser(String name) {
        this.name = name;
        this.version = "latest";
        this.headless = false;
    }
    
    // Constructor with all parameters
    public Browser(String name, String version, boolean headless) {
        this.name = name;
        this.version = version;
        this.headless = headless;
    }
    
    public void launch() {
        System.out.println("Launching " + name + " " + version);
    }
}

Method Overloading

Multiple methods with the same name but different parameters.

class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

Static Members

class TestConfig {
    // Static variables - shared across all instances
    static String baseUrl = "https://example.com";
    static int timeout = 30;
    static int testCount = 0;
    
    String testName;
    
    public TestConfig(String testName) {
        this.testName = testName;
        testCount++;
    }
    
    // Static method - can be called without object
    public static void printConfig() {
        System.out.println("Base URL: " + baseUrl);
        System.out.println("Tests created: " + testCount);
    }
}

Interactive Exercise: Create a Class

💻 Build a User Class

Create a User class with username, email, and a method to display info:

🔒 Encapsulation

Encapsulation is the practice of hiding internal object details and providing controlled access through public methods (getters/setters). This is a core principle of the Page Object Model in test automation.

🎯 Encapsulation in QA:
  • Page Objects: Hide element locators, expose only actions
  • Test Data: Protect sensitive data with private fields
  • Framework Design: Control how tests interact with utilities
  • Maintainability: Change implementation without breaking tests

Access Modifiers

Control visibility and access to class members:

Modifier Class Package Subclass World
public
protected
default (no modifier)
private

Getters and Setters

class User {
    // Private variables (encapsulated)
    private String username;
    private String password;
    private int age;
    
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    // Getter methods (read access)
    public String getUsername() {
        return username;
    }
    
    public int getAge() {
        return age;
    }
    
    // Setter methods with validation (controlled write access)
    public void setAge(int age) {
        if (age > 0 && age < 150) {
            this.age = age;
        } else {
            System.out.println("Invalid age");
        }
    }
    
    // Password verification (not direct access)
    public boolean verifyPassword(String inputPassword) {
        return this.password.equals(inputPassword);
    }
}
🎯 Why Encapsulation?
  • Data protection - prevent invalid values
  • Flexibility - change internal implementation without affecting users
  • Maintainability - easier to debug and modify
  • Security - hide sensitive data like passwords

Interactive Exercise: Encapsulation

💻 Create Encapsulated Class

Create a BankAccount class with private balance and public methods:

🌳 Inheritance

Inheritance allows a class to inherit properties and methods from another class, promoting code reuse.

Basic Inheritance

// Parent class (Base class / Superclass)
class WebElement {
    protected String locator;
    protected String type;
    
    public WebElement(String locator, String type) {
        this.locator = locator;
        this.type = type;
    }
    
    public void click() {
        System.out.println("Clicking " + type + ": " + locator);
    }
    
    public void isDisplayed() {
        System.out.println("Checking if " + type + " is displayed");
    }
}

// Child class (Derived class / Subclass)
class Button extends WebElement {
    public Button(String locator) {
        super(locator, "Button");  // Call parent constructor
    }
    
    // Additional method specific to Button
    public void doubleClick() {
        System.out.println("Double clicking button: " + locator);
    }
}

class TextBox extends WebElement {
    public TextBox(String locator) {
        super(locator, "TextBox");
    }
    
    public void enterText(String text) {
        System.out.println("Entering '" + text + "' in: " + locator);
    }
}

public class Main {
    public static void main(String[] args) {
        Button btn = new Button("#submit");
        btn.click();          // Inherited method
        btn.doubleClick();    // Own method
        
        TextBox input = new TextBox("#username");
        input.enterText("admin");
        input.isDisplayed();  // Inherited method
    }
}

Method Overriding

class TestCase {
    protected String name;
    
    public TestCase(String name) {
        this.name = name;
    }
    
    public void setUp() {
        System.out.println("Base setup for: " + name);
    }
    
    public void execute() {
        System.out.println("Executing test: " + name);
    }
}

class WebTest extends TestCase {
    public WebTest(String name) {
        super(name);
    }
    
    // Override parent method
    @Override
    public void setUp() {
        super.setUp();  // Call parent method
        System.out.println("Launching browser...");
    }
    
    @Override
    public void execute() {
        System.out.println("Running web test: " + name);
    }
}
💡 Inheritance in Test Automation:
  • BaseTest class with common setup/teardown
  • PageObject parent class with common methods
  • TestData hierarchy for different data types
  • Reporter classes with specialized formatting

🎭 Polymorphism

Polymorphism means "many forms" - the ability to process objects differently based on their type.

Method Overriding (Runtime Polymorphism)

class Report {
    public void generate() {
        System.out.println("Generating basic report");
    }
}

class HTMLReport extends Report {
    @Override
    public void generate() {
        System.out.println("Generating HTML report with styling");
    }
}

class PDFReport extends Report {
    @Override
    public void generate() {
        System.out.println("Generating PDF report");
    }
}

public class Main {
    public static void main(String[] args) {
        // Polymorphism - same reference type, different behaviors
        Report report1 = new Report();
        Report report2 = new HTMLReport();
        Report report3 = new PDFReport();
        
        report1.generate();  // Basic report
        report2.generate();  // HTML report
        report3.generate();  // PDF report
    }
}

Practical Example: Test Framework

class Browser {
    public void launch() {
        System.out.println("Launching browser");
    }
}

class ChromeBrowser extends Browser {
    @Override
    public void launch() {
        System.out.println("Starting ChromeDriver");
        System.out.println("Launching Chrome browser");
    }
}

class FirefoxBrowser extends Browser {
    @Override
    public void launch() {
        System.out.println("Starting GeckoDriver");
        System.out.println("Launching Firefox browser");
    }
}

class TestRunner {
    public static void runTest(Browser browser) {
        browser.launch();  // Polymorphic behavior
        System.out.println("Running test...");
    }
}

public class Main {
    public static void main(String[] args) {
        Browser chrome = new ChromeBrowser();
        Browser firefox = new FirefoxBrowser();
        
        TestRunner.runTest(chrome);
        TestRunner.runTest(firefox);
    }
}

Interactive Exercise: Polymorphism

💻 Create Shape Hierarchy

Create Shape parent class with Circle and Rectangle children, each calculating area:

🎯 Benefits of Polymorphism:
  • Code flexibility - easily add new types
  • Maintainability - changes in one place
  • Extensibility - add features without modifying existing code
  • Clean architecture - program to interfaces, not implementations