#การทดสอบซอฟต์แวร์ด้วย Selenium Java และ JUnit

การทดสอบเว็บแบบอัตโนมัติ หรือ Web UI Test Automation ช่วยให้เราสามารถจำลองพฤติกรรมของผู้ใช้ เช่น เปิดหน้าเว็บ กรอก Username/Password คลิกปุ่ม Login และตรวจสอบผลลัพธ์ได้โดยอัตโนมัติ

บทความนี้สาธิตการใช้

  • Java สำหรับเขียน Test Script
  • Selenium WebDriver สำหรับควบคุม Browser
  • JUnit Jupiter สำหรับจัดการ Test Case และ Assertion
  • Maven สำหรับจัดการ Dependency และรัน Test
  • Selenium Manager สำหรับจัดการ Browser Driver อัตโนมัติ

เว็บที่ใช้ทดลองคือ

https://seleniumbase.io/simple/login

ข้อมูล Login สำหรับหน้า Demo คือ

Username: demo_user
Password: secret_pass

บทความนี้ใช้ Selenium Java 4.49.0, JUnit Jupiter 6.1.3 และ Maven Surefire 3.6.0 ซึ่งเป็นเวอร์ชันปัจจุบัน ณ วันที่จัดทำบทความ หากใช้ในอนาคตสามารถปรับเลขเวอร์ชันใน pom.xml ให้เป็นเวอร์ชันล่าสุดได้


#1. Selenium คืออะไร

Selenium เป็นชุดเครื่องมือสำหรับทำ Browser Automation โดย Selenium WebDriver สามารถสั่ง Browser ให้ทำงานเหมือนผู้ใช้จริง เช่น

  • เปิด URL
  • ค้นหา Element
  • กรอกข้อมูลใน Form
  • คลิก Button/Link
  • อ่านข้อความบนหน้าเว็บ
  • ตรวจสอบผลลัพธ์
  • ทำ Screenshot
  • รันแบบ Headless
  • ใช้งานร่วมกับ CI/CD

Selenium รองรับ Browser หลัก เช่น

  • Google Chrome
  • Mozilla Firefox
  • Microsoft Edge
  • Safari

#2. JUnit คืออะไร

JUnit เป็น Testing Framework สำหรับภาษา Java

เมื่อนำมาใช้ร่วมกับ Selenium เราสามารถแยกขั้นตอนการทดสอบออกเป็น Test Case และใช้ Annotation เช่น

@Test
@BeforeEach
@AfterEach

รวมถึง Assertion เช่น

assertEquals()
assertTrue()
assertNotNull()

เพื่อยืนยันว่าผลลัพธ์จากระบบตรงกับสิ่งที่คาดหวัง

แนวคิดการทำงานโดยรวมคือ

JUnit
   |
   v
Test Case
   |
   v
Selenium WebDriver
   |
   v
Browser
   |
   v
Web Application

#3. สิ่งที่ต้องติดตั้ง

แนะนำให้ใช้

JDK 17+
Maven 3.6.3+
Google Chrome
Git (ถ้าต้องการ)
IDE เช่น IntelliJ IDEA / VS Code / Eclipse

ตรวจสอบ Java

java -version

ตรวจสอบ Java Compiler

javac -version

ตรวจสอบ Maven

mvn -version

#4. ติดตั้ง Java และ Maven บน macOS

หากใช้ Homebrew สามารถติดตั้ง OpenJDK และ Maven ได้ดังนี้

brew install openjdk@17
brew install maven

ตรวจสอบ

java -version
mvn -version

หากระบบยังไม่พบ Java อาจต้องเพิ่ม JDK เข้า PATH ตามคำแนะนำของ Homebrew

ตัวอย่าง

export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH"

สำหรับ Mac Intel อาจเป็น

export PATH="/usr/local/opt/openjdk@17/bin:$PATH"

#5. ติดตั้ง Java และ Maven บน Windows

สามารถใช้ winget

winget install EclipseAdoptium.Temurin.17.JDK

ติดตั้ง Maven

winget install Apache.Maven

จากนั้นเปิด Terminal ใหม่และตรวจสอบ

java -version
mvn -version

หาก mvn หรือ java ยังไม่ถูกพบ ให้ตรวจสอบค่า Environment Variables ได้แก่

JAVA_HOME
PATH

#6. สร้าง Project

สร้าง Folder สำหรับโปรเจกต์

mkdir selenium-java-junit
cd selenium-java-junit

สร้างโครงสร้างดังนี้

selenium-java-junit/
├── pom.xml
└── src/
    └── test/
        └── java/
            └── com/
                └── example/
                    └── LoginTest.java

บน macOS/Linux สามารถใช้

mkdir -p src/test/java/com/example

บน Windows PowerShell

New-Item -ItemType Directory -Force src/test/java/com/example

#7. สร้างไฟล์ pom.xml

สร้างไฟล์

pom.xml

แล้วเพิ่ม

<?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
            https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>selenium-java-junit</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.release>17</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <selenium.version>4.49.0</selenium.version>
        <junit.version>6.1.3</junit.version>
        <surefire.version>3.6.0</surefire.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire.version}</version>
            </plugin>

        </plugins>
    </build>

</project>

#8. Dependency ที่สำคัญ

#Selenium Java

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

ใช้สำหรับ

WebDriver
ChromeDriver
By
WebElement
WebDriverWait
ExpectedConditions

#JUnit Jupiter

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>6.1.3</version>
    <scope>test</scope>
</dependency>

ใช้สำหรับ

@Test
@BeforeEach
@AfterEach
Assertions

#9. Selenium Manager

ใน Selenium รุ่นใหม่ เราไม่จำเป็นต้อง Download chromedriver ด้วยตนเองในกรณีใช้งานทั่วไป

ดังนั้นไม่จำเป็นต้องเขียน

System.setProperty(
    "webdriver.chrome.driver",
    "/path/to/chromedriver"
);

สามารถสร้าง Browser ได้โดยตรง

WebDriver driver = new ChromeDriver();

Selenium จะใช้ Selenium Manager เพื่อช่วยค้นหาและจัดการ Driver ที่เหมาะสมให้อัตโนมัติ

ดังนั้นโค้ดเริ่มต้นจึงสั้นลงมาก

WebDriver driver = new ChromeDriver();

driver.get("https://seleniumbase.io/simple/login");

driver.quit();

#10. สำรวจหน้าเว็บที่จะทดสอบ

เปิด

https://seleniumbase.io/simple/login

หน้าเว็บมีข้อมูลหลัก ได้แก่

Username
Password
Sign in

ตัวอย่าง Locator ที่เราจะใช้คือ

Username: #username
Password: #password
Sign in:  #log-in

หลัง Login สำเร็จ สามารถตรวจข้อความ

Welcome!

จาก Element

<h1>

และมีรูปภาพที่สามารถระบุด้วย

#image1

#11. Test Scenario

เราจะสร้าง Test Scenario ดังนี้

1. เปิดหน้า Login
2. กรอก Username = demo_user
3. กรอก Password = secret_pass
4. คลิก Sign in
5. ตรวจสอบว่ามีข้อความ Welcome!
6. ตรวจสอบว่ารูป #image1 แสดงอยู่
7. คลิก Sign out
8. ตรวจสอบข้อความ signed out

เขียนในรูปแบบ Given / When / Then ได้ดังนี้

Given
ผู้ใช้เปิดหน้า Login

When
ผู้ใช้กรอก Username และ Password ที่ถูกต้อง
และกด Sign in

Then
ระบบต้องแสดงข้อความ Welcome!

When
ผู้ใช้กด Sign out

Then
ระบบต้องแสดงข้อความว่า signed out

#12. เขียน Test แรกด้วย Selenium + JUnit

สร้างไฟล์

src/test/java/com/example/LoginTest.java

เพิ่มโค้ด

package com.example;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

import static org.junit.jupiter.api.Assertions.assertEquals;

class LoginTest {

    private WebDriver driver;

    @BeforeEach
    void setUp() {
        driver = new ChromeDriver();
    }

    @AfterEach
    void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    void shouldLoginSuccessfully() {

        driver.get("https://seleniumbase.io/simple/login");

        driver.findElement(By.id("username"))
                .sendKeys("demo_user");

        driver.findElement(By.id("password"))
                .sendKeys("secret_pass");

        driver.findElement(By.id("log-in"))
                .click();

        String heading = driver.findElement(By.tagName("h1"))
                .getText();

        assertEquals("Welcome!", heading);
    }
}

#13. อธิบาย Code

#สร้าง WebDriver

driver = new ChromeDriver();

คำสั่งนี้เปิด Google Chrome และสร้าง Selenium Session


#เปิดหน้าเว็บ

driver.get("https://seleniumbase.io/simple/login");

เทียบได้กับการพิมพ์ URL ใน Address Bar ของ Browser


#ค้นหา Username

driver.findElement(By.id("username"))

Selenium จะหา Element ที่มี

id="username"

แล้วกรอกข้อมูลด้วย

.sendKeys("demo_user");

#กรอก Password

driver.findElement(By.id("password"))
        .sendKeys("secret_pass");

#คลิก Sign in

driver.findElement(By.id("log-in"))
        .click();

#อ่านข้อความจาก h1

String heading = driver.findElement(By.tagName("h1"))
        .getText();

#Assertion

assertEquals("Welcome!", heading);

JUnit จะเปรียบเทียบ

Expected = Welcome!
Actual   = ข้อความที่ Selenium อ่านได้

ถ้าตรงกัน Test จะ Pass

ถ้าไม่ตรงกัน Test จะ Fail


#14. รัน Test

ใช้คำสั่ง

mvn test

Maven จะ

อ่าน pom.xml
      ↓
Download Dependencies
      ↓
Compile Test
      ↓
JUnit Platform
      ↓
Run LoginTest
      ↓
Selenium เปิด Chrome
      ↓
ทดสอบหน้า Login
      ↓
แสดงผล Pass / Fail

ตัวอย่างผลลัพธ์

Tests run: 1,
Failures: 0,
Errors: 0,
Skipped: 0

#15. รันเฉพาะ Test Class

mvn -Dtest=LoginTest test

#16. รันเฉพาะ Test Method

mvn -Dtest=LoginTest#shouldLoginSuccessfully test

บน shell บางชนิด # อาจมีความหมายพิเศษ ให้ครอบค่าด้วย quote หากจำเป็น

ตัวอย่าง

mvn "-Dtest=LoginTest#shouldLoginSuccessfully" test

#17. ปรับปรุง Test ด้วย WebDriverWait

ในการทดสอบจริง ไม่ควรพึ่งการทำงานทันทีหลัง Click เพราะหน้าเว็บอาจต้องใช้เวลาโหลด

แนวทางที่ดีกว่าคือใช้ Explicit Wait

package com.example;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.assertEquals;

class LoginTest {

    private WebDriver driver;
    private WebDriverWait wait;

    @BeforeEach
    void setUp() {

        driver = new ChromeDriver();

        wait = new WebDriverWait(
                driver,
                Duration.ofSeconds(10)
        );
    }

    @AfterEach
    void tearDown() {

        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    void shouldLoginSuccessfully() {

        driver.get(
            "https://seleniumbase.io/simple/login"
        );

        wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.id("username")
            )
        ).sendKeys("demo_user");

        driver.findElement(By.id("password"))
                .sendKeys("secret_pass");

        driver.findElement(By.id("log-in"))
                .click();

        String heading = wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.tagName("h1")
            )
        ).getText();

        assertEquals("Welcome!", heading);
    }
}

ข้อดีคือ Test ไม่ต้องหยุดแบบตายตัวด้วย

Thread.sleep(...)

แต่จะรอจนกว่า Condition ที่กำหนดจะสำเร็จ หรือ Timeout


#18. ทดสอบ Login และ Logout

ปรับ Test ให้ครบ Flow

@Test
void shouldLoginAndLogoutSuccessfully() {

    driver.get(
        "https://seleniumbase.io/simple/login"
    );

    wait.until(
        ExpectedConditions.visibilityOfElementLocated(
            By.id("username")
        )
    ).sendKeys("demo_user");

    driver.findElement(By.id("password"))
            .sendKeys("secret_pass");

    driver.findElement(By.id("log-in"))
            .click();

    String heading = wait.until(
        ExpectedConditions.visibilityOfElementLocated(
            By.tagName("h1")
        )
    ).getText();

    assertEquals("Welcome!", heading);

    wait.until(
        ExpectedConditions.visibilityOfElementLocated(
            By.id("image1")
        )
    );

    wait.until(
        ExpectedConditions.elementToBeClickable(
            By.linkText("Sign out")
        )
    ).click();

    String message = wait.until(
        ExpectedConditions.visibilityOfElementLocated(
            By.id("top_message")
        )
    ).getText();

    org.junit.jupiter.api.Assertions.assertTrue(
        message.toLowerCase().contains("signed out")
    );
}

#19. Test Class ฉบับสมบูรณ์

ตัวอย่างที่เหมาะสำหรับใช้เป็น Workshop

package com.example;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class LoginTest {

    private WebDriver driver;
    private WebDriverWait wait;

    private static final String BASE_URL =
            "https://seleniumbase.io/simple/login";

    @BeforeEach
    void setUp() {

        ChromeOptions options = new ChromeOptions();

        if (Boolean.getBoolean("headless")) {
            options.addArguments("--headless=new");
            options.addArguments("--window-size=1920,1080");
        }

        driver = new ChromeDriver(options);

        wait = new WebDriverWait(
                driver,
                Duration.ofSeconds(10)
        );
    }

    @AfterEach
    void tearDown() {

        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    @DisplayName("Login ด้วยข้อมูลที่ถูกต้อง")
    void shouldLoginSuccessfully() {

        driver.get(BASE_URL);

        wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.id("username")
            )
        ).sendKeys("demo_user");

        driver.findElement(By.id("password"))
                .sendKeys("secret_pass");

        driver.findElement(By.id("log-in"))
                .click();

        String heading = wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.tagName("h1")
            )
        ).getText();

        assertEquals("Welcome!", heading);

        assertTrue(
            driver.findElement(By.id("image1"))
                    .isDisplayed()
        );
    }

    @Test
    @DisplayName("Login แล้ว Logout ได้สำเร็จ")
    void shouldLoginAndLogoutSuccessfully() {

        driver.get(BASE_URL);

        wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.id("username")
            )
        ).sendKeys("demo_user");

        driver.findElement(By.id("password"))
                .sendKeys("secret_pass");

        driver.findElement(By.id("log-in"))
                .click();

        wait.until(
            ExpectedConditions.textToBe(
                By.tagName("h1"),
                "Welcome!"
            )
        );

        wait.until(
            ExpectedConditions.elementToBeClickable(
                By.linkText("Sign out")
            )
        ).click();

        String message = wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.id("top_message")
            )
        ).getText();

        assertTrue(
            message.toLowerCase().contains("signed out")
        );
    }
}

#20. รันแบบ Headless

การรันแบบ Headless เหมาะกับ

  • CI/CD
  • GitHub Actions
  • Jenkins
  • GitLab CI
  • Docker
  • Server ที่ไม่มี Desktop GUI

จากโค้ดตัวอย่าง เราเปิด Headless ผ่าน System Property

mvn -Dheadless=true test

ใน Java ตรวจสอบด้วย

if (Boolean.getBoolean("headless")) {
    options.addArguments("--headless=new");
}

เมื่อรันแบบปกติ

mvn test

Browser จะเปิดให้เห็น

เมื่อรัน

mvn -Dheadless=true test

Browser จะทำงานเบื้องหลัง


#21. แนะนำให้หลีกเลี่ยง Thread.sleep()

ตัวอย่างที่ไม่ควรใช้เป็นแนวทางหลัก

Thread.sleep(5000);

ปัญหาคือ

ถ้าเว็บโหลด 1 วินาที
Test ก็ยังต้องรอ 5 วินาที

ถ้าเว็บโหลดเกิน 5 วินาที
Test อาจ Fail

ควรใช้

WebDriverWait

ร่วมกับ

ExpectedConditions

เช่น

wait.until(
    ExpectedConditions.elementToBeClickable(
        By.id("log-in")
    )
);

#22. Locator ที่นิยมใช้

Selenium รองรับ Locator หลายแบบ

#ID

By.id("username")

#Name

By.name("username")

#CSS Selector

By.cssSelector("#username")

#XPath

By.xpath("//input[@id='username']")

#Link Text

By.linkText("Sign out")

#Tag Name

By.tagName("h1")

โดยทั่วไป หาก Element มี id ที่ชัดเจน ควรใช้

By.id(...)

เพราะอ่านง่ายและดูแลรักษาง่าย


#23. CSS Selector กับ Selenium

ตัวอย่าง

driver.findElement(
    By.cssSelector("#username")
);

เทียบเท่า

driver.findElement(
    By.id("username")
);

ตัวอย่าง Selector อื่น

#username
#password
#log-in
img#image1

#24. Page Object Model

เมื่อจำนวน Test เพิ่มขึ้น ไม่ควรใส่ Locator และ Action ทั้งหมดไว้ใน Test Class

สามารถใช้ Page Object Model (POM)

โครงสร้าง

src/test/java/
└── com/example/
    ├── pages/
    │   └── LoginPage.java
    └── tests/
        └── LoginTest.java

#25. สร้าง LoginPage

สร้าง

src/test/java/com/example/pages/LoginPage.java
package com.example.pages;

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

public class LoginPage {

    private final WebDriver driver;

    private final By username =
            By.id("username");

    private final By password =
            By.id("password");

    private final By loginButton =
            By.id("log-in");

    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    public LoginPage open() {

        driver.get(
            "https://seleniumbase.io/simple/login"
        );

        return this;
    }

    public LoginPage enterUsername(String value) {

        driver.findElement(username)
                .sendKeys(value);

        return this;
    }

    public LoginPage enterPassword(String value) {

        driver.findElement(password)
                .sendKeys(value);

        return this;
    }

    public void clickLogin() {

        driver.findElement(loginButton)
                .click();
    }

    public void login(
            String username,
            String password
    ) {

        enterUsername(username);
        enterPassword(password);
        clickLogin();
    }
}

#26. Test ด้วย Page Object

package com.example.tests;

import com.example.pages.LoginPage;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

import static org.junit.jupiter.api.Assertions.assertEquals;

class LoginTest {

    private WebDriver driver;

    @BeforeEach
    void setUp() {
        driver = new ChromeDriver();
    }

    @AfterEach
    void tearDown() {

        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    void shouldLoginSuccessfully() {

        LoginPage loginPage =
                new LoginPage(driver);

        loginPage
                .open()
                .enterUsername("demo_user")
                .enterPassword("secret_pass")
                .clickLogin();

        String heading =
                driver.findElement(
                        By.tagName("h1")
                ).getText();

        assertEquals(
                "Welcome!",
                heading
        );
    }
}

ข้อดีของ Page Object Model คือ

Test Code
    |
    v
Page Object
    |
    v
Selenium
    |
    v
Browser

เมื่อหน้าเว็บเปลี่ยน Locator เราสามารถแก้ที่ Page Object เป็นหลัก โดยไม่ต้องแก้ Test ทุกไฟล์


#27. โครงสร้าง Project ที่แนะนำ

selenium-java-junit/
├── pom.xml
└── src/
    └── test/
        └── java/
            └── com/
                └── example/
                    ├── pages/
                    │   └── LoginPage.java
                    └── tests/
                        └── LoginTest.java

เมื่อระบบมีหลายหน้า สามารถเพิ่ม

pages/
├── LoginPage.java
├── DashboardPage.java
├── ProfilePage.java
└── ProductPage.java

และ

tests/
├── LoginTest.java
├── ProfileTest.java
└── ProductTest.java

#28. รายงานผลการทดสอบ

หลังรัน

mvn test

Maven Surefire จะเก็บผลไว้ที่

target/surefire-reports/

ตรวจสอบ

ls target/surefire-reports

Windows

Get-ChildItem target/surefire-reports

โดยทั่วไปจะพบไฟล์ประเภท

TEST-com.example.LoginTest.xml
com.example.LoginTest.txt

#29. Lifecycle ของ JUnit Test

เมื่อมี Test Method หลายตัว

@Test
void testA() {}

@Test
void testB() {}

JUnit จะทำงานโดยประมาณดังนี้

@BeforeEach
   ↓
testA
   ↓
@AfterEach

@BeforeEach
   ↓
testB
   ↓
@AfterEach

ดังนั้นแต่ละ Test สามารถเปิด Browser Session ใหม่เพื่อเพิ่มความเป็นอิสระระหว่าง Test ได้


#30. อย่าลืม driver.quit()

ควรมี

@AfterEach
void tearDown() {

    if (driver != null) {
        driver.quit();
    }
}

ถ้าไม่ปิด Driver อาจเกิด

  • Chrome Process ค้าง
  • ใช้ Memory เพิ่มขึ้น
  • CI Runner มี Process สะสม
  • Test รอบถัดไปมีปัญหา

#31. ปัญหาที่พบบ่อย

#mvn: command not found

แสดงว่า Maven ยังไม่ได้ติดตั้ง หรือ PATH ยังไม่ถูกต้อง

ตรวจสอบ

mvn -version

#java: command not found

ตรวจสอบ

java -version

และค่า

JAVA_HOME

#Browser เปิดไม่ได้

ตรวจสอบว่า Chrome ถูกติดตั้ง

และทดลองสร้าง Driver แบบง่าย

WebDriver driver =
        new ChromeDriver();

driver.get(
    "https://example.com"
);

driver.quit();

#TimeoutException

มักเกิดจาก Element ยังไม่พร้อม

ควรใช้

WebDriverWait

แทนการค้นหา Element ทันที


#NoSuchElementException

ตรวจสอบ Locator

ตัวอย่าง

By.id("username")

ว่า Element ในหน้าเว็บยังใช้ ID เดิมหรือไม่


#32. Workflow ของ Selenium + Java + JUnit

ภาพรวม

Developer / Tester
       |
       v
Java Test Code
       |
       v
JUnit Jupiter
       |
       v
Selenium WebDriver
       |
       v
Selenium Manager
       |
       v
ChromeDriver
       |
       v
Google Chrome
       |
       v
Web Application

#33. Workflow เมื่อรันด้วย Maven

mvn test
   |
   v
Maven Surefire
   |
   v
JUnit Platform
   |
   v
LoginTest
   |
   v
@BeforeEach
   |
   v
ChromeDriver
   |
   v
Selenium Test Steps
   |
   v
Assertions
   |
   v
@AfterEach
   |
   v
Test Report

#34. คำสั่งที่ใช้บ่อย

ติดตั้ง Dependency และรัน Test

mvn test

Clean แล้วรัน Test

mvn clean test

รัน Test Class เดียว

mvn -Dtest=LoginTest test

รันแบบ Headless

mvn -Dheadless=true test

ดู Dependency

mvn dependency:tree

ลบ Build Output

mvn clean

#35. แนวทางพัฒนาต่อ

หลังจากเข้าใจตัวอย่างนี้แล้ว สามารถต่อยอดไปยัง

Selenium
   |
   +-- Page Object Model
   |
   +-- Data-Driven Testing
   |
   +-- Parameterized Tests
   |
   +-- Screenshots on Failure
   |
   +-- Parallel Testing
   |
   +-- Selenium Grid
   |
   +-- Docker
   |
   +-- GitHub Actions
   |
   +-- Jenkins
   |
   +-- Allure Report

#36. สรุป

การสร้าง Web UI Test ด้วย Java สามารถเริ่มได้ด้วย Stack ที่เรียบง่าย

Java
+
Selenium WebDriver
+
JUnit Jupiter
+
Maven

Selenium ทำหน้าที่ควบคุม Browser

Open
Click
Type
Read
Wait

JUnit ทำหน้าที่จัดการ Test Lifecycle และ Assertion

@BeforeEach
@Test
@AfterEach
assertEquals
assertTrue

Maven ทำหน้าที่จัดการ Dependency และรัน Test

mvn test

และ Selenium Manager ช่วยลดขั้นตอนการจัดการ Browser Driver ทำให้การเริ่มต้น Selenium Java ในปัจจุบันไม่จำเป็นต้องดาวน์โหลด chromedriver และกำหนด Path ด้วยตนเองในกรณีทั่วไป

ตัวอย่างนี้จึงเป็นจุดเริ่มต้นที่ดีสำหรับการพัฒนา Automated UI Testing ก่อนต่อยอดไปสู่ Page Object Model, CI/CD, Selenium Grid และการทดสอบระดับ Production


#Source Code ฉบับย่อ

package com.example;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

import static org.junit.jupiter.api.Assertions.assertEquals;

class LoginTest {

    WebDriver driver;

    @BeforeEach
    void setUp() {
        driver = new ChromeDriver();
    }

    @AfterEach
    void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    void loginTest() {

        driver.get(
            "https://seleniumbase.io/simple/login"
        );

        driver.findElement(
            By.id("username")
        ).sendKeys("demo_user");

        driver.findElement(
            By.id("password")
        ).sendKeys("secret_pass");

        driver.findElement(
            By.id("log-in")
        ).click();

        assertEquals(
            "Welcome!",
            driver.findElement(
                By.tagName("h1")
            ).getText()
        );
    }
}

รันด้วย

mvn test

#อ้างอิง