- บทนำ
- 1. Data-Driven Testing คืออะไร
- 2. ข้อดีของ Data-Driven Testing
- 3. เครื่องมือที่ใช้
- 4. สร้าง Maven Project
- 5. กำหนด pom.xml
- 6. เว็บไซต์สำหรับทดลอง
- 7. ทดลอง Selenium Test แบบธรรมดาก่อน
- 8. Data-Driven Testing ด้วย @ValueSource
- 9. Data-Driven Testing ด้วย @CsvSource
- 10. Data-Driven Testing จากไฟล์ CSV
- 11. อ่าน CSV ด้วย @CsvFileSource
- 12. ลด Code Duplication ด้วย @BeforeEach และ @AfterEach
- 13. เพิ่ม Explicit Wait
- 14. แยก Page Object
- 15. Test Class หลังใช้ Page Object
- 16. ตัวอย่าง Data Set สำหรับ Boundary Testing
- 17. รองรับข้อมูลหลายชนิด
- 18. Data Source อื่นที่ใช้ร่วมกับ Selenium ได้
- 19. รัน Test
- 20. ตัวอย่างผลการรัน
- 21. แนวทางออกแบบ Data-Driven Test ที่ดี
- 22. Data-Driven Testing กับ CI/CD
- 23. Architecture ที่แนะนำ
- 24. @CsvSource กับ @CsvFileSource ต่างกันอย่างไร
- 25. สรุป
การทดสอบเว็บด้วยข้อมูลหลายชุด โดยใช้ Selenium WebDriver + JUnit Jupiter Parameterized Test + CSV
#บทนำ
ในการทำ UI Automation Testing เรามักพบว่าขั้นตอนการทดสอบเหมือนเดิม แต่ข้อมูลที่ใช้ทดสอบแตกต่างกัน เช่น
- ชื่อผู้ใช้หลายรูปแบบ
- ค่าขอบเขตต่ำสุด/สูงสุด
- ข้อมูลภาษาไทย/อังกฤษ
- ข้อมูลปกติและข้อมูลผิดรูปแบบ
- ข้อมูลหลายชุดจาก CSV, JSON, Excel หรือ Database
หากเราเขียน Test Case แยกสำหรับข้อมูลทุกชุด จะเกิดโค้ดซ้ำจำนวนมาก
Data-Driven Testing (DDT) คือแนวทางที่แยก Test Logic ออกจาก Test Data แล้วนำข้อมูลแต่ละชุดมารันกับ Test Logic เดิมซ้ำ ๆ
แนวคิดโดยย่อ:
Test Data
│
├── Row 1
├── Row 2
├── Row 3
└── Row N
│
▼
Parameterized Test
│
▼
Selenium WebDriver
│
▼
Browser
│
▼
Assertion / Test Result
บทความนี้ใช้:
- Java 17+
- Maven
- Selenium Java 4.49.0
- JUnit Jupiter 6.1.3
- Google Chrome
- Maven Surefire Plugin 3.6.0
หมายเหตุ: Selenium รุ่นปัจจุบันมี Selenium Manager รวมมาให้แล้ว โดย Selenium bindings จะเรียกใช้อัตโนมัติเพื่อจัดการ browser driver ดังนั้นโดยทั่วไปไม่จำเป็นต้องดาวน์โหลด
chromedriverด้วยตนเอง
#1. Data-Driven Testing คืออะไร
สมมติเราต้องทดสอบฟอร์มหนึ่งชุดด้วยข้อมูลต่อไปนี้
| Test Data | Expected Result |
|---|---|
Selenium |
Received! |
Data Driven Testing |
Received! |
NPRU |
Received! |
0123456789 |
Received! |
ถ้าเขียนแบบปกติ อาจต้องสร้างหลาย @Test
@Test
void testSelenium() {
// steps...
}
@Test
void testDataDrivenTesting() {
// steps...
}
@Test
void testNpru() {
// steps...
}
ปัญหาคือ Test Logic ซ้ำกัน
DDT จะเปลี่ยนเป็นแนวคิดนี้
@ParameterizedTest
void testForm(String input, String expected) {
// Test Logic เขียนเพียงครั้งเดียว
}
จากนั้นให้ Framework ส่งข้อมูลแต่ละชุดเข้ามาเอง
#2. ข้อดีของ Data-Driven Testing
#ลด Code Duplication
Test Steps เขียนครั้งเดียว แต่รันได้กับข้อมูลจำนวนมาก
#เพิ่ม Test Coverage
เพิ่ม Test Case ได้โดยเพิ่มข้อมูล ไม่จำเป็นต้องเพิ่ม Test Method ใหม่
#แยก Test Data ออกจาก Test Logic
ทีม QA สามารถแก้ CSV ได้โดยไม่ต้องแก้ WebDriver flow
#เหมาะกับ Boundary Testing
ตัวอย่าง:
0
1
99
100
101
#รองรับ Positive / Negative Testing
ตัวอย่าง:
valid@example.com
invalid-email
(empty)
very-long-email
#3. เครื่องมือที่ใช้
ตรวจสอบ Java
java --version
ตรวจสอบ Maven
mvn --version
ควรมี Google Chrome ติดตั้งอยู่ในเครื่อง
#4. สร้าง Maven Project
สร้างโครงสร้างโปรเจกต์
selenium-data-driven/
├── pom.xml
└── src
└── test
├── java
│ └── com
│ └── example
│ └── DataDrivenTest.java
└── resources
└── test-data.csv
สร้างโฟลเดอร์บน macOS / Linux:
mkdir -p selenium-data-driven/src/test/java/com/example
mkdir -p selenium-data-driven/src/test/resources
cd selenium-data-driven
Windows PowerShell:
mkdir selenium-data-driven
cd selenium-data-driven
mkdir src\test\java\com\example
mkdir src\test\resources
#5. กำหนด 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-data-driven</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>
</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>3.6.0</version>
</plugin>
</plugins>
</build>
</project>
ดาวน์โหลด Dependency
mvn test-compile
#6. เว็บไซต์สำหรับทดลอง
ตัวอย่างนี้ใช้ Selenium Web Form
https://www.selenium.dev/selenium/web/web-form.html
Element สำคัญ ได้แก่
<input name="my-text">
<button>Submit</button>
<div id="message">Received!</div>
#7. ทดลอง Selenium Test แบบธรรมดาก่อน
สร้างไฟล์
src/test/java/com/example/DataDrivenTest.java
ตัวอย่างพื้นฐาน:
package com.example;
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 DataDrivenTest {
@Test
void normalTest() {
WebDriver driver = new ChromeDriver();
try {
driver.get(
"https://www.selenium.dev/selenium/web/web-form.html"
);
driver.findElement(By.name("my-text"))
.sendKeys("Selenium");
driver.findElement(By.cssSelector("button"))
.click();
String message =
driver.findElement(By.id("message"))
.getText();
assertEquals("Received!", message);
} finally {
driver.quit();
}
}
}
รัน
mvn test
#8. Data-Driven Testing ด้วย @ValueSource
กรณี Test Method รับ Parameter เพียงค่าเดียว สามารถใช้ @ValueSource
package com.example;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
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 DataDrivenTest {
@ParameterizedTest
@ValueSource(strings = {
"Selenium",
"Java",
"JUnit",
"Data Driven Testing"
})
void testWithValueSource(String inputText) {
WebDriver driver = new ChromeDriver();
try {
driver.get(
"https://www.selenium.dev/selenium/web/web-form.html"
);
driver.findElement(By.name("my-text"))
.sendKeys(inputText);
driver.findElement(By.cssSelector("button"))
.click();
String actual =
driver.findElement(By.id("message"))
.getText();
assertEquals("Received!", actual);
} finally {
driver.quit();
}
}
}
JUnit จะเรียก Test Method เดิมทั้งหมด 4 ครั้ง
Selenium
Java
JUnit
Data Driven Testing
#9. Data-Driven Testing ด้วย @CsvSource
ถ้าต้องการส่ง Parameter หลายค่า สามารถใช้ @CsvSource
package com.example;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
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 DataDrivenTest {
@ParameterizedTest(name =
"[{index}] input={0}, expected={1}"
)
@CsvSource({
"Selenium, Received!",
"'Data Driven Testing', Received!",
"NPRU, Received!",
"0123456789, Received!"
})
void testWithCsvSource(
String inputText,
String expectedMessage) {
WebDriver driver = new ChromeDriver();
try {
driver.get(
"https://www.selenium.dev/selenium/web/web-form.html"
);
driver.findElement(By.name("my-text"))
.sendKeys(inputText);
driver.findElement(By.cssSelector("button"))
.click();
String actualMessage =
driver.findElement(By.id("message"))
.getText();
assertEquals(
expectedMessage,
actualMessage
);
} finally {
driver.quit();
}
}
}
ข้อดีคือข้อมูลแต่ละ Row กลายเป็น Test Invocation แยกกัน
#10. Data-Driven Testing จากไฟล์ CSV
สำหรับโครงการจริง การแยกข้อมูลออกเป็นไฟล์ภายนอกจะดูแลรักษาง่ายกว่า
สร้างไฟล์
src/test/resources/test-data.csv
เนื้อหา:
inputText,expectedMessage
Selenium,Received!
Data Driven Testing,Received!
NPRU,Received!
0123456789,Received!
Software Testing,Received!
#11. อ่าน CSV ด้วย @CsvFileSource
package com.example;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
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 DataDrivenTest {
@ParameterizedTest(
name = "[{index}] {0} -> {1}"
)
@CsvFileSource(
resources = "/test-data.csv",
numLinesToSkip = 1
)
void testFormWithCsvFile(
String inputText,
String expectedMessage) {
WebDriver driver = new ChromeDriver();
try {
driver.get(
"https://www.selenium.dev/selenium/web/web-form.html"
);
driver.findElement(By.name("my-text"))
.sendKeys(inputText);
driver.findElement(By.cssSelector("button"))
.click();
String actualMessage =
driver.findElement(By.id("message"))
.getText();
assertEquals(
expectedMessage,
actualMessage
);
} finally {
driver.quit();
}
}
}
JUnit จะอ่าน CSV ทีละแถวและสร้าง Test Invocation ตามจำนวนข้อมูล
#12. ลด Code Duplication ด้วย @BeforeEach และ @AfterEach
ตัวอย่างที่เหมาะกับงานจริงมากขึ้น:
package com.example;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
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 DataDrivenTest {
private WebDriver driver;
@BeforeEach
void setUp() {
driver = new ChromeDriver();
driver.manage()
.window()
.maximize();
}
@AfterEach
void tearDown() {
if (driver != null) {
driver.quit();
}
}
@ParameterizedTest(
name = "[{index}] input={0}"
)
@CsvFileSource(
resources = "/test-data.csv",
numLinesToSkip = 1
)
void testForm(
String inputText,
String expectedMessage) {
driver.get(
"https://www.selenium.dev/selenium/web/web-form.html"
);
driver.findElement(By.name("my-text"))
.sendKeys(inputText);
driver.findElement(By.cssSelector("button"))
.click();
String actualMessage =
driver.findElement(By.id("message"))
.getText();
assertEquals(
expectedMessage,
actualMessage
);
}
}
ผลคือ Test Method เหลือเฉพาะ Business/Test Logic ที่สำคัญ
#13. เพิ่ม Explicit Wait
Web Application จริงมักมี AJAX, Animation หรือ JavaScript ที่ทำให้ Element ยังไม่พร้อมทันที
สามารถใช้ WebDriverWait
import java.time.Duration;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
สร้าง Wait
WebDriverWait wait =
new WebDriverWait(
driver,
Duration.ofSeconds(10)
);
รอข้อความหลัง Submit
String actualMessage =
wait.until(
ExpectedConditions
.visibilityOfElementLocated(
By.id("message")
)
).getText();
ตัวอย่าง:
@ParameterizedTest
@CsvFileSource(
resources = "/test-data.csv",
numLinesToSkip = 1
)
void testForm(
String inputText,
String expectedMessage) {
WebDriverWait wait =
new WebDriverWait(
driver,
Duration.ofSeconds(10)
);
driver.get(
"https://www.selenium.dev/selenium/web/web-form.html"
);
wait.until(
ExpectedConditions
.visibilityOfElementLocated(
By.name("my-text")
)
).sendKeys(inputText);
driver.findElement(
By.cssSelector("button")
).click();
String actualMessage =
wait.until(
ExpectedConditions
.visibilityOfElementLocated(
By.id("message")
)
).getText();
assertEquals(
expectedMessage,
actualMessage
);
}
#14. แยก Page Object
เมื่อ Test Suite ใหญ่ขึ้น ไม่ควรใส่ Locator และ WebDriver Steps ทั้งหมดไว้ใน Test Class
สร้าง
src/test/java/com/example/pages/WebFormPage.java
package com.example.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class WebFormPage {
private final WebDriver driver;
private final By textInput =
By.name("my-text");
private final By submitButton =
By.cssSelector("button");
private final By resultMessage =
By.id("message");
public WebFormPage(WebDriver driver) {
this.driver = driver;
}
public void open() {
driver.get(
"https://www.selenium.dev/selenium/web/web-form.html"
);
}
public void enterText(String text) {
driver.findElement(textInput)
.sendKeys(text);
}
public void submit() {
driver.findElement(submitButton)
.click();
}
public String getMessage() {
return driver.findElement(resultMessage)
.getText();
}
}
#15. Test Class หลังใช้ Page Object
package com.example;
import com.example.pages.WebFormPage;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import static org.junit.jupiter.api.Assertions.assertEquals;
class WebFormDataDrivenTest {
private WebDriver driver;
private WebFormPage page;
@BeforeEach
void setUp() {
driver = new ChromeDriver();
page = new WebFormPage(driver);
}
@AfterEach
void tearDown() {
if (driver != null) {
driver.quit();
}
}
@ParameterizedTest(
name = "[{index}] {0}"
)
@CsvFileSource(
resources = "/test-data.csv",
numLinesToSkip = 1
)
void testWebForm(
String input,
String expected) {
page.open();
page.enterText(input);
page.submit();
assertEquals(
expected,
page.getMessage()
);
}
}
โครงสร้างจะชัดเจนขึ้น
Test Data
│
▼
Parameterized Test
│
▼
Page Object
│
▼
Selenium WebDriver
│
▼
Browser
#16. ตัวอย่าง Data Set สำหรับ Boundary Testing
ไฟล์
boundary-data.csv
inputText,expectedMessage
A,Received!
AB,Received!
1234567890,Received!
ข้อความภาษาไทย,Received!
special_!@#$%,Received!
Test:
@ParameterizedTest
@CsvFileSource(
resources = "/boundary-data.csv",
numLinesToSkip = 1
)
void boundaryTest(
String input,
String expected) {
page.open();
page.enterText(input);
page.submit();
assertEquals(
expected,
page.getMessage()
);
}
#17. รองรับข้อมูลหลายชนิด
JUnit Parameterized Test รองรับ Argument Source หลายแบบ เช่น
@ValueSource
@NullSource
@EmptySource
@NullAndEmptySource
@EnumSource
@MethodSource
@CsvSource
@CsvFileSource
@ArgumentsSource
ตัวอย่าง @NullAndEmptySource
@ParameterizedTest
@NullAndEmptySource
void testEmptyInput(String input) {
// validation test
}
ตัวอย่าง @MethodSource
static Stream<Arguments> testData() {
return Stream.of(
Arguments.of(
"Selenium",
"Received!"
),
Arguments.of(
"Java",
"Received!"
)
);
}
ใช้งาน
@ParameterizedTest
@MethodSource("testData")
void testForm(
String input,
String expected) {
}
@MethodSource เหมาะเมื่อข้อมูลต้องสร้างแบบ Dynamic หรือเป็น Object ที่ซับซ้อน
#18. Data Source อื่นที่ใช้ร่วมกับ Selenium ได้
ในระบบจริง Test Data อาจมาจากหลายแหล่ง
CSV
│
Excel
│
JSON
│
Database
│
REST API
│
Generated Data
│
▼
Test Data Provider
│
▼
Parameterized Test
│
▼
Selenium WebDriver
ตัวอย่างเครื่องมือเสริม:
| Data Source | Java Library |
|---|---|
| CSV | JUnit @CsvFileSource, Apache Commons CSV |
| Excel | Apache POI |
| JSON | Jackson / Gson |
| Database | JDBC |
| REST API | Java HttpClient / REST Assured |
| Generated Data | Java Faker |
#19. รัน Test
รันทั้งหมด
mvn test
รัน Test Class
mvn -Dtest=DataDrivenTest test
รัน Test Class ที่ใช้ Page Object
mvn -Dtest=WebFormDataDrivenTest test
#20. ตัวอย่างผลการรัน
ผลโดยประมาณ:
[INFO] Running com.example.WebFormDataDrivenTest
[1] Selenium
[2] Data Driven Testing
[3] NPRU
[4] 0123456789
[5] Software Testing
Tests run: 5
Failures: 0
Errors: 0
Skipped: 0
BUILD SUCCESS
#21. แนวทางออกแบบ Data-Driven Test ที่ดี
#21.1 อย่าใส่ Test Logic ใน CSV
ไม่ควร:
username,password,clickButton,waitSecond,locator
...
CSV ควรเก็บเฉพาะ Test Data และ Expected Result
ตัวอย่างที่เหมาะกว่า:
username,password,expectedResult
user01,password123,SUCCESS
user02,wrong,INVALID_PASSWORD
unknown,password123,USER_NOT_FOUND
Test Logic ควรอยู่ใน Java
#21.2 ทุก Row ควรมีความหมาย
แต่ละแถวควรแทน Test Scenario ที่ต้องการจริง
เช่น:
Valid
Invalid
Boundary
Empty
Special Character
Unicode
Long Input
ไม่ควรเพิ่มข้อมูลจำนวนมากโดยไม่มีเป้าหมาย
#21.3 Test ควร Independent
แต่ละ Dataset ไม่ควรพึ่งผลของ Dataset ก่อนหน้า
ไม่ควร:
Row 1 = Create User
Row 2 = Login User
Row 3 = Delete User
เพราะหาก Row 1 ล้มเหลว Row อื่นอาจล้มเหลวตาม
#21.4 เปิด Browser ใหม่ต่อ Test Invocation
รูปแบบ
@BeforeEach
void setUp() {
driver = new ChromeDriver();
}
ช่วยลด State Leakage เช่น
Cookie
Session
Local Storage
Previous Page State
#22. Data-Driven Testing กับ CI/CD
เมื่อ Test พร้อมแล้วสามารถรันใน Pipeline
ตัวอย่าง GitHub Actions:
name: Selenium Tests
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: maven
- name: Run Selenium Tests
run: mvn test
สำหรับ CI ควรใช้ Chrome Headless
ตัวอย่าง:
ChromeOptions options =
new ChromeOptions();
options.addArguments("--headless=new");
driver = new ChromeDriver(options);
#23. Architecture ที่แนะนำ
สำหรับโครงการขนาดกลางถึงใหญ่:
src/test/java
│
├── tests
│ ├── LoginTest.java
│ ├── RegisterTest.java
│ └── CheckoutTest.java
│
├── pages
│ ├── LoginPage.java
│ ├── RegisterPage.java
│ └── CheckoutPage.java
│
├── utils
│ ├── DriverFactory.java
│ └── TestDataLoader.java
│
└── models
└── TestData.java
src/test/resources
│
├── login-data.csv
├── register-data.csv
└── checkout-data.csv
Flow:
Test Data
│
▼
Parameterized Test
│
▼
Page Object
│
▼
Driver Factory
│
▼
Selenium WebDriver
│
▼
Chrome / Firefox / Edge
#24. @CsvSource กับ @CsvFileSource ต่างกันอย่างไร
| หัวข้อ | @CsvSource |
@CsvFileSource |
|---|---|---|
| ที่เก็บข้อมูล | ใน Java code | CSV file |
| เหมาะกับ | Dataset เล็ก | Dataset กลาง/ใหญ่ |
| แก้ข้อมูลโดยไม่แก้ Java | ไม่ได้ | ได้ |
| อ่านง่ายเมื่อข้อมูลเยอะ | น้อยกว่า | ดีกว่า |
| ใช้ใน CI/CD | ได้ | ได้ |
| Maintenance | ง่ายเมื่อข้อมูลน้อย | เหมาะกับงานจริงกว่า |
คำแนะนำ:
1–5 rows
→ @CsvSource
หลาย rows / เปลี่ยนข้อมูลบ่อย
→ @CsvFileSource
ข้อมูลซับซ้อน / Dynamic
→ @MethodSource หรือ Custom Provider
#25. สรุป
Data-Driven Testing ช่วยเปลี่ยนรูปแบบจาก
1 Test
+
1 Data Set
ให้เป็น
1 Test Logic
+
Many Data Sets
แนวทางที่แนะนำสำหรับ Selenium Java คือ
CSV
↓
JUnit Parameterized Test
↓
Page Object
↓
Selenium WebDriver
↓
Browser
↓
Assertion
เริ่มต้นง่ายที่สุดด้วย
@ParameterizedTest
@CsvSource(...)
และเมื่อโครงการใหญ่ขึ้นให้แยกข้อมูลออกเป็น
@CsvFileSource(...)
จากนั้นสามารถขยายไปสู่
Excel
JSON
Database
REST API
Dynamic Test Data
ได้โดยไม่จำเป็นต้องเปลี่ยน WebDriver Test Logic หลักมากนัก
#References
- Selenium WebDriver documentation: https://www.selenium.dev/documentation/webdriver/
- Selenium first script: https://www.selenium.dev/documentation/webdriver/getting_started/first_script/
- Selenium Manager: https://www.selenium.dev/documentation/selenium_manager/
- Selenium Java on Maven Central: https://central.sonatype.com/artifact/org.seleniumhq.selenium/selenium-java
- JUnit documentation: https://docs.junit.org/
- JUnit Jupiter on Maven Central: https://central.sonatype.com/artifact/org.junit.jupiter/junit-jupiter