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-SwagLabsStep 2: Run Example Test
mvn test -Dtest=TS_Ordering_JUnitStep 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_JUnitPage 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.
| Method | Purpose |
|---|---|
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_JUnitTest 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.htmlReport Locations
| Report Type | Location |
|---|---|
| Allure Results | target/allure-results/ |
| Screenshots | target/screenshots/ |
| Test Data CSV | target/data/TestStepsData.csv |
| HTTP Reports | target/http-reports/ |
| JavaDocs | target/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=trueCleanup 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 reportCommon 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:
- ✅ Write your first 3 tests
- ✅ Run them successfully
- ✅ Review reports
- ✅ Share with your team
Need More?
| Resource | Description |
|---|---|
| Complete Reading Guide | Full documentation path |
| DevOps Guide | CI/CD integration |
| Framework Maintainer Guide | Extending framework |
| Test Development Guide | Detailed reference |