Test Writer's Guide

Get productive writing tests in 90 minutes

Welcome Test Writers!

Target Audience: QA Engineers, Test Automation Engineers, SDET

Reading Time: 90 minutes | Goal: Write and run automated tests

This guide gets you writing tests FAST. We skip the heavy architecture stuff and focus on what you need:

  • ✅ Writing tests using Page Objects
  • ✅ Using assertions that auto-screenshot
  • ✅ Running tests
  • ✅ Viewing reports
  • ✅ Basic troubleshooting

Quick Start (5 Minutes)

Goal: See a test run successfully

Step 1: Verify Setup

# Check Java version (need 23+)
java -version

# Check Maven
mvn -version

# Navigate to project
cd path/to/Automation-JAV-SwagLabs

Step 2: Run Example Test

mvn test -Dtest=TS_Ordering_JUnit

Step 3: Check Results

allure serve target/allure-results

✅ Success? Continue to Your First Test | ❌ Errors? See Troubleshooting section

Your First Test (20 Minutes)

Goal: Write a complete test from scratch

Step 1: Create Page Object

File: src/test/java/pageobjects/PO_ExamplePage.java

package pageobjects;

import org.openqa.selenium.WebDriver;
import framework.automation.FW_Page;
import framework.junit.FW_CustomAssertJU;

public class PO_ExamplePage extends FW_Page {
    
    // Constructor
    public PO_ExamplePage(WebDriver driver) {
        super(driver);
    }
    
    // Locators (use Selenium locator syntax)
    private String btn_submit = "id=submit-button";
    private String txt_username = "name=username";
    private String txt_password = "css=input[type='password']";
    private String lbl_welcomeMessage = "xpath=//h1[@class='welcome']";
    
    // Actions
    public void navigateToPage() {
        String url = "https://example.com/login";
        FW_CustomAssertJU.navigateToURL(driver, url);
    }
    
    public void enterUsername(String username) {
        FW_CustomAssertJU.sendKeys(driver, txt_username, username);
    }
    
    public void enterPassword(String password) {
        FW_CustomAssertJU.sendKeys(driver, txt_password, password);
    }
    
    public void clickSubmit() {
        FW_CustomAssertJU.click(driver, btn_submit);
    }
    
    public void verifyWelcomeMessage() {
        FW_CustomAssertJU.elementExists(driver, lbl_welcomeMessage);
    }
}

Step 2: Create Test Suite

File: src/test/java/testsuites/TS_MyFirstTests_JUnit.java

package testsuites;

import org.junit.jupiter.api.*;
import org.openqa.selenium.WebDriver;
import pageobjects.PO_ExamplePage;
import framework.automation.FW_Driver;
import framework.junit.FW_TestSuite;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TS_MyFirstTests_JUnit extends FW_TestSuite {
    
    // Page Objects
    PO_ExamplePage examplePage;
    
    @BeforeAll
    void setupSuite() {
        // Initialize WebDriver
        driver = FW_Driver.createDriver("chrome", false);
        
        // Initialize Page Objects
        examplePage = new PO_ExamplePage(driver);
    }
    
    @AfterAll
    void teardownSuite() {
        // Close browser
        if (driver != null) {
            driver.quit();
        }
    }
    
    @Test
    @Order(1)
    void tc_login_successful() {
        // Navigate to page
        examplePage.navigateToPage();
        
        // Enter credentials
        examplePage.enterUsername("testuser");
        examplePage.enterPassword("password123");
        
        // Click submit
        examplePage.clickSubmit();
        
        // Verify welcome message
        examplePage.verifyWelcomeMessage();
    }
}

Step 3: Run Your Test

mvn test -Dtest=TS_MyFirstTests_JUnit

Page Object Pattern

The Page Object Model ↗ (POM) separates test logic from page structure.

Structure

  • Locators: Element identification (id, xpath, css)
  • Constructor: Initialize with WebDriver
  • Actions: Methods that interact with page

Benefits

  • Reusable page interactions
  • Easy maintenance when UI changes
  • Cleaner, more readable tests

Locator Syntax

// ID
private String btn_login = "id=login-button";

// Name
private String txt_username = "name=user";

// CSS Selector
private String div_content = "css=div.content";

// XPath
private String lnk_logout = "xpath=//a[@class='logout']";

// Link Text
private String lnk_home = "linkText=Home";

// Partial Link Text
private String lnk_settings = "partialLinkText=Sett";

Best Practices

✅ DO: Use descriptive locator names (btn_submit, txt_email) | Use prefixes: btn_, txt_, lbl_, lnk_, chk_, drp_ | Keep actions simple | Use FW_CustomAssertJU for interactions

❌ DON'T: Put assertions in Page Objects | Make locators public | Use Thread.sleep() | Hardcode test data in Page Objects

Custom Assertions

Use FW_CustomAssertJU for assertions that automatically capture screenshots.

MethodPurpose
autoPass(driver, locator, description)Click element, pass on success
autoPass(driver, locator, text, description)Enter text, pass on success
autoFail(driver, locator, description)Verify element NOT present
assertTrue(condition, message)Assert condition is true
click(driver, locator)Click element with screenshot on failure
sendKeys(driver, locator, text)Enter text with screenshot on failure
elementExists(driver, locator)Verify element is present
verifyTextEquals(driver, locator, expected)Verify element text matches
navigateToURL(driver, url)Navigate to URL
selectByVisibleText(driver, locator, text)Select dropdown option

Why FW_CustomAssertJU?

// Standard Selenium - No screenshot on failure ❌
driver.findElement(By.id("button")).click();

// Framework Custom Assertion - Auto-screenshot on failure ✅
FW_CustomAssertJU.click(driver, "id=button");

Running Tests

# Run all tests
mvn clean verify

# Run specific test suite
mvn test -Dtest=TS_Example_JUnit

# Run specific test method
mvn test -Dtest=TS_Example_JUnit#tc_login_valid_user

# Run by tag
mvn test -Dgroups="smoke"

# Run headless
mvn test -Dbrowser.headless=true

# Run multiple suites
mvn test -Dtest=TS_Login_JUnit,TS_Registration_JUnit

Test Organization

File Structure

src/test/java/
├── pageobjects/           # Page Objects (PO_*.java)
│   ├── PO_LoginPage.java
│   ├── PO_HomePage.java
│   └── PO_ProductPage.java
├── testsuites/            # Test Suites (TS_*_JUnit.java)
│   ├── TS_Login_JUnit.java
│   ├── TS_Registration_JUnit.java
│   └── TS_Checkout_JUnit.java
└── framework/             # Framework classes (read-only)
    ├── automation/
    ├── junit/
    └── utilities/

Using Tags

@Test
@Tag("smoke")
@Tag("login")
void tc_login_valid_user() {
    // Test code
}

// Run smoke tests only
mvn test -Dgroups="smoke"

// Run login tests only
mvn test -Dgroups="login"

Viewing Reports

# Serve Allure report (opens browser)
allure serve target/allure-results

# Generate static report
allure generate target/allure-results -o target/allure-report

# View JavaDocs
mvn site
open target/site/index.html

Report Locations

Report TypeLocation
Allure Resultstarget/allure-results/
Screenshotstarget/screenshots/
Test Data CSVtarget/data/TestStepsData.csv
HTTP Reportstarget/http-reports/
JavaDocstarget/site/index.html

Common Patterns

Pattern 1: Login Flow

@Test
void tc_login_valid_credentials() {
    loginPage.navigateToLogin();
    loginPage.enterUsername("standard_user");
    loginPage.enterPassword("secret_sauce");
    loginPage.clickLogin();
    homePage.verifyUserLoggedIn();
}

Pattern 2: Form Submission

@Test
void tc_complete_registration() {
    registrationPage.navigateToRegistration();
    registrationPage.enterFirstName("John");
    registrationPage.enterLastName("Doe");
    registrationPage.enterEmail("john@example.com");
    registrationPage.selectCountry("United States");
    registrationPage.agreeToTerms();
    registrationPage.clickSubmit();
    registrationPage.verifySuccessMessage();
}

Pattern 3: Data-Driven Test

@Test
void tc_login_with_multiple_users() {
    String[][] users = {
        {"user1", "pass1"},
        {"user2", "pass2"},
        {"user3", "pass3"}
    };
    
    for (String[] user : users) {
        performLogin(user[0], user[1]);
        homePage.verifyUserLoggedIn();
        homePage.logout();
    }
}

Pattern 4: Wait for Element

// Built into FW_CustomAssertJU (implicit wait)
FW_CustomAssertJU.elementExists(driver, "id=slow-element");

// Custom explicit wait (if needed)
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("element")));

Pattern 5: Conditional Logic

@Test
void tc_handle_optional_popup() {
    loginPage.navigateToPage();
    
    // Check if popup exists (doesn't fail if not found)
    boolean popupExists = driver.findElements(By.id("popup")).size() > 0;
    
    if (popupExists) {
        FW_CustomAssertJU.click(driver, "id=closePopup");
    }
    
    // Continue with test
    loginPage.enterUsername("testuser");
}

Troubleshooting

Element not found

Cause: Wrong locator or timing issue

Fix: Verify locator in browser DevTools, add wait

ChromeDriver error

Cause: Version mismatch

Fix: Update Chrome or ChromeDriver to match

Test timeout

Cause: Slow page load

Fix: Increase timeout in testConfig.properties

Stale element

Cause: Page refreshed

Fix: Re-find element after navigation

Test won't run

Cause: Missing annotation or naming

Fix: Ensure class ends with _JUnit.java and has @Test

Allure report won't open

Cause: Allure not installed

Fix: brew install allure (Mac) or scoop install allure (Windows)

Tests Pass Locally, Fail in CI

Common Causes: Different browser versions, Network speed differences, Screen resolution differences

Solutions: Use headless mode in CI, Increase timeouts, Use explicit waits for dynamic content

Command Reference

Maven Test Commands

# Run all tests
mvn clean verify

# Run specific suite
mvn test -Dtest=TS_Login_JUnit

# Run multiple suites
mvn test -Dtest=TS_Login_JUnit,TS_Registration_JUnit

# Run single test case
mvn test -Dtest=TS_Login_JUnit#tc_login_successful

# Skip tests
mvn clean install -DskipTests

# Run with system properties
mvn test -Dbrowser=firefox -Dheadless=true

Cleanup Commands

# Clean build artifacts
mvn clean

# Remove test data
rm -rf target/data/*

# Remove screenshots
rm -rf target/screenshots/*

Best Practices

Test Writing

  • ✅ Use descriptive test names (tc_login_with_valid_credentials)
  • ✅ One assertion per test when possible
  • ✅ Keep tests independent (can run in any order)
  • ✅ Use Page Objects for all interactions
  • ✅ Add @Order to control execution sequence
  • ❌ Don't use Thread.sleep() (use waits instead)
  • ❌ Don't hardcode test data
  • ❌ Don't test multiple things in one test

Page Objects

  • ✅ One Page Object per page/component
  • ✅ Use descriptive locator names
  • ✅ Keep actions simple (one thing per method)
  • ✅ Return void or boolean (not elements)
  • ❌ Don't put assertions in Page Objects
  • ❌ Don't make locators public
  • ❌ Don't return WebElements

Advanced (Optional)

HTTP Security Tests

File: Doc-HTTP-Assessment.html

import framework.audits.FW_Audit_HttpReview;
import framework.http.FW_Http;

@Test
void tc_security_audit() {
    String url = "https://example.com";
    FW_Http http = new FW_Http(driver);
    
    // Perform HTTP audit
    FW_Audit_HttpReview.performAudit(driver, url, true);
    
    // Report generated in target/reports/
}

What It Checks: HTTPS/SSL configuration, Security headers (CSP, HSTS, X-Frame-Options), Cookie security, Server configuration

Network Diagnostic Tests

File: Doc-Network-Diagnostics.html

import framework.audits.FW_Audit_Ping;

@Test
void tc_ping_test() {
    String host = "www.google.com";
    FW_Audit_Ping.performPing(driver, host, 4, 1000);
}

Available Tests: Ping (check host reachability), Traceroute (network path analysis), Speed Test (bandwidth measurement)

Quick Reference Card

Print this section!

Common Commands

mvn test -Dtest=TS_MyTests_JUnit        # Run suite
mvn test -Dtest=*_JUnit#tc_specific     # Run test
allure serve target/allure-results       # View report

Common Assertions

FW_CustomAssertJU.navigateToURL(driver, url);
FW_CustomAssertJU.click(driver, locator);
FW_CustomAssertJU.sendKeys(driver, locator, text);
FW_CustomAssertJU.elementExists(driver, locator);
FW_CustomAssertJU.verifyTextEquals(driver, locator, expected);

Locator Syntax

"id=element-id"
"name=element-name"
"css=.class-name"
"xpath=//div[@class='test']"

Test Structure

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TS_Name_JUnit extends FW_TestSuite {
    @BeforeAll void setupSuite() { }
    @AfterAll void teardownSuite() { }
    @Test @Order(1) void tc_test() { }
}

Next Steps

You've completed the Test Writer's Guide! 🎉

Now:

  1. ✅ Write your first 3 tests
  2. ✅ Run them successfully
  3. ✅ Review reports
  4. ✅ Share with your team

Need More?

ResourceDescription
Complete Reading GuideFull documentation path
DevOps GuideCI/CD integration
Framework Maintainer GuideExtending framework
Test Development GuideDetailed reference