Selenium #
Pengujian manual antarmuka web adalah pekerjaan yang membosankan, tidak konsisten, dan tidak bisa diskalakan. Seorang developer bisa menghabiskan berjam-jam mengklik form, mengisi input, dan memverifikasi hasil — dan masih bisa melewatkan edge case. Selenium WebDriver hadir sebagai solusi: ia memungkinkan kamu mengendalikan browser sungguhan (Chrome, Firefox, Edge, Safari) secara programatik dari kode Java. Setiap klik, isian form, navigasi halaman, dan verifikasi konten bisa dituliskan sebagai kode yang bisa dijalankan berulang kali dengan hasil yang konsisten. Di luar testing, Selenium juga digunakan untuk web scraping — mengekstrak data dari halaman yang membutuhkan JavaScript untuk merender kontennya, sesuatu yang tidak bisa dilakukan oleh HTTP client biasa. Dalam artikel ini, kita akan membangun fondasi yang kuat untuk menulis test Selenium yang andal, mudah dimaintain, dan bebas dari anti-pattern paling umum yang membuat test Selenium menjadi lambat dan flaky.
Cara Kerja Selenium WebDriver #
Selenium WebDriver berkomunikasi dengan browser melalui protokol WebDriver (W3C standard). Setiap browser memiliki driver tersendiri yang bertindak sebagai jembatan antara kode Java dan browser.
flowchart LR
JAVA[Kode Java\nSelenium WebDriver] -->|WebDriver Protocol\nHTTP/JSON| DRIVER[ChromeDriver /\nGeckoDriver /\nEdgeDriver]
DRIVER -->|DevTools Protocol\natau native API| BROWSER[Browser\nChrome / Firefox / Edge]
BROWSER -->|DOM events| DRIVER
DRIVER -->|response JSON| JAVASelenium 4 memperkenalkan WebDriverManager yang menghilangkan kebutuhan mengunduh dan mengkonfigurasi driver secara manual — library otomatis mendeteksi versi browser yang terinstal dan mengunduh driver yang sesuai.
Setup Dependencies #
<!-- Maven -->
<dependencies>
<!-- Selenium WebDriver -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.21.0</version>
</dependency>
<!-- JUnit 5 untuk test runner -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- AssertJ untuk assertion yang ekspresif -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.3</version>
<scope>test</scope>
</dependency>
</dependencies>
// Gradle
dependencies {
implementation 'org.seleniumhq.selenium:selenium-java:4.21.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
testImplementation 'org.assertj:assertj-core:3.25.3'
}
Inisialisasi WebDriver #
Selenium 4 menyertakan Selenium Manager bawaan yang otomatis mengunduh dan mengkonfigurasi driver yang sesuai — kamu tidak perlu lagi mengunduh chromedriver.exe secara manual atau menggunakan WebDriverManager pihak ketiga.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.edge.EdgeDriver;
public class WebDriverFactory {
// Chrome — driver paling umum digunakan
public static WebDriver buatChrome() {
ChromeOptions options = new ChromeOptions();
// Opsi yang berguna untuk CI/CD environment
options.addArguments("--no-sandbox"); // wajib di Docker/Linux
options.addArguments("--disable-dev-shm-usage"); // cegah crash di container
options.addArguments("--window-size=1920,1080");
// ✗ ANTI-PATTERN: mode headless lama — tidak merender beberapa fitur CSS
// options.addArguments("--headless");
// ✓ BENAR: headless mode baru (Selenium 4.8+) — lebih akurat
options.addArguments("--headless=new");
return new ChromeDriver(options);
}
// Firefox
public static WebDriver buatFirefox() {
FirefoxOptions options = new FirefoxOptions();
options.addArguments("--headless");
options.addArguments("--width=1920");
options.addArguments("--height=1080");
return new FirefoxDriver(options);
}
// Buat driver berdasarkan environment variable
// Berguna untuk menjalankan test di browser berbeda tanpa ubah kode
public static WebDriver buatDariEnv() {
String browser = System.getenv().getOrDefault("BROWSER", "chrome");
return switch (browser.toLowerCase()) {
case "firefox" -> buatFirefox();
case "edge" -> new EdgeDriver();
default -> buatChrome();
};
}
}
Konfigurasi Dasar dan Teardown #
import org.junit.jupiter.api.*;
import org.openqa.selenium.WebDriver;
import java.time.Duration;
public class BaseTest {
protected WebDriver driver;
@BeforeEach
void setUp() {
driver = WebDriverFactory.buatChrome();
// Implicit wait — berapa lama driver menunggu elemen muncul secara default
// ✗ ANTI-PATTERN: implicit wait tinggi — memperlambat test saat elemen tidak ada
// driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(30));
// ✓ BENAR: implicit wait singkat, gunakan explicit wait untuk kondisi spesifik
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));
// Timeout untuk page load
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));
// Maksimalkan jendela browser
driver.manage().window().maximize();
}
@AfterEach
void tearDown() {
// WAJIB: tutup browser setelah setiap test
// Jika tidak, browser akan terus terakumulasi dan menghabiskan memori
if (driver != null) {
driver.quit(); // quit() menutup SEMUA jendela + membunuh proses driver
// Jangan gunakan driver.close() — hanya menutup tab aktif
}
}
}
Menemukan Elemen — Locator Strategy #
Memilih locator yang tepat adalah keterampilan inti Selenium. Locator yang buruk membuat test rapuh (flaky) — bisa lulus hari ini dan gagal besok karena perubahan kecil pada halaman.
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
public class LocatorDemo {
public void contohLocator(WebDriver driver) {
// ✗ HINDARI: XPath absolut — pecah ketika struktur HTML berubah
driver.findElement(By.xpath("/html/body/div[1]/div[2]/form/input[1]"));
// ✗ HINDARI: class yang dibuat otomatis oleh framework CSS (angka acak)
driver.findElement(By.className("css-1x7ibqp")); // tidak stabil
// ✓ TERBAIK: ID — selalu unik di halaman, tercepat
driver.findElement(By.id("username"));
// ✓ SANGAT BAIK: data-testid — atribut khusus untuk testing, tidak berubah saat styling
driver.findElement(By.cssSelector("[data-testid='tombol-login']"));
// ✓ BAIK: CSS selector — lebih cepat dari XPath, lebih fleksibel dari ID
driver.findElement(By.cssSelector("form.login-form input[type='email']"));
driver.findElement(By.cssSelector("button[type='submit']"));
// ✓ BAIK: name attribute — umum untuk form input
driver.findElement(By.name("password"));
// ✓ BOLEH: XPath relatif dengan teks — untuk elemen yang tidak punya ID/name
driver.findElement(By.xpath("//button[contains(text(),'Masuk')]"));
driver.findElement(By.xpath("//label[text()='Email']/following-sibling::input"));
// ✓ BAIK: link text — untuk anchor tag
driver.findElement(By.linkText("Lupa password?"));
driver.findElement(By.partialLinkText("Lupa"));
}
}
Urutan prioritas locator dari terbaik ke terburuk:
1. ID → By.id("...") — unik, cepat, stabil
2. data-testid / data-cy → By.cssSelector("[data-testid='...']") — dibuat khusus test
3. name → By.name("...") — bagus untuk form
4. CSS selector → By.cssSelector("...") — fleksibel, cepat
5. Link text → By.linkText("...") — untuk anchor saja
6. XPath relatif → By.xpath("//...") — fleksibel, lebih lambat
7. XPath absolut → /html/body/div/... — HINDARI — sangat rapuh
Interaksi Dasar dengan Elemen #
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.support.ui.Select;
public class InteraksiDasar {
public void demo(WebDriver driver) {
driver.get("https://contoh.com/login");
// Klik elemen
WebElement tombolLogin = driver.findElement(By.id("btn-login"));
tombolLogin.click();
// Isi input teks
WebElement inputEmail = driver.findElement(By.id("email"));
inputEmail.clear(); // bersihkan dulu jika ada nilai lama
inputEmail.sendKeys("[email protected]");
// Tekan tombol keyboard
inputEmail.sendKeys(Keys.TAB); // pindah ke field berikutnya
driver.findElement(By.id("password")).sendKeys("rahasia123" + Keys.ENTER);
// Ambil teks elemen
WebElement pesan = driver.findElement(By.className("pesan-sukses"));
String teks = pesan.getText();
System.out.println("Pesan: " + teks);
// Ambil atribut
WebElement gambar = driver.findElement(By.tagName("img"));
String src = gambar.getAttribute("src");
String altText = gambar.getAttribute("alt");
// Cek kondisi elemen
boolean tampil = tombolLogin.isDisplayed();
boolean bisaDiklik = tombolLogin.isEnabled();
boolean tercentang = driver.findElement(By.id("checkbox-setuju")).isSelected();
// Dropdown dengan Select
WebElement dropdownEl = driver.findElement(By.id("kota"));
Select dropdown = new Select(dropdownEl);
dropdown.selectByVisibleText("Jakarta"); // pilih berdasarkan teks
dropdown.selectByValue("jkt"); // pilih berdasarkan value
dropdown.selectByIndex(2); // pilih berdasarkan urutan
String pilihanSaatIni = dropdown.getFirstSelectedOption().getText();
// Multiple select
if (dropdown.isMultiple()) {
dropdown.selectByVisibleText("Bandung");
dropdown.selectByVisibleText("Surabaya");
}
// Navigasi
driver.navigate().to("https://contoh.com/dashboard");
driver.navigate().back();
driver.navigate().forward();
driver.navigate().refresh();
// Ambil informasi halaman
String judulHalaman = driver.getTitle();
String urlSaatIni = driver.getCurrentUrl();
System.out.println("Halaman: " + judulHalaman + " | URL: " + urlSaatIni);
}
}
Explicit Wait — Menunggu dengan Cerdas #
Ini adalah konsep terpenting untuk test Selenium yang andal. Halaman web modern bersifat asinkron — elemen muncul setelah AJAX request selesai, animasi, atau kondisi lain. Menunggu dengan waktu tetap (Thread.sleep) adalah anti-pattern.
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.TimeoutException;
public class ExplicitWaitDemo {
public void demo(WebDriver driver) {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// WebDriverWait akan polling setiap 500ms sampai kondisi terpenuhi atau timeout
// ✗ ANTI-PATTERN: Thread.sleep — menunggu waktu tetap, tidak peduli kondisi
// Jika aplikasi lambat, test gagal. Jika cepat, waktu terbuang.
try { Thread.sleep(3000); } catch (InterruptedException e) { }
// ✓ BENAR: tunggu sampai elemen ada di DOM (belum tentu terlihat)
WebElement elemen = wait.until(
ExpectedConditions.presenceOfElementLocated(By.id("hasil"))
);
// ✓ BENAR: tunggu sampai elemen terlihat dan bisa diklik
WebElement tombol = wait.until(
ExpectedConditions.elementToBeClickable(By.id("btn-submit"))
);
// ✓ BENAR: tunggu sampai teks tertentu muncul di elemen
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.id("status"), "Berhasil disimpan"
));
// ✓ BENAR: tunggu sampai URL berubah (setelah submit form)
wait.until(ExpectedConditions.urlContains("/dashboard"));
// ✓ BENAR: tunggu sampai elemen HILANG (loading spinner)
wait.until(ExpectedConditions.invisibilityOfElementLocated(
By.className("loading-spinner")
));
// ✓ BENAR: tunggu sampai jumlah elemen dalam list bertambah
wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(
By.cssSelector(".produk-item"), 0
));
// Kondisi custom — untuk kasus yang tidak ditangani ExpectedConditions bawaan
wait.until(d -> {
String teks = d.findElement(By.id("counter")).getText();
return Integer.parseInt(teks) > 5;
});
// Menangani timeout dengan graceful
try {
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("opsional")));
} catch (TimeoutException e) {
System.out.println("Elemen opsional tidak muncul, lanjutkan test");
}
}
}
Page Object Model (POM) #
Page Object Model adalah pola desain wajib untuk test Selenium yang bisa dimaintain. Alih-alih menulis locator dan aksi langsung di test, encapsulate semua detail halaman dalam kelas terpisah. Test menjadi lebih bersih, mudah dibaca, dan mudah diperbarui ketika UI berubah.
flowchart LR
subgraph TEST[Test Class]
T1[LoginTest\nberanda → login → verifikasi]
end
subgraph POM[Page Objects]
P1["LoginPage\nusernameField\npasswordField\ntombolLogin\npesan()"]
P2["DashboardPage\njudulSelamatDatang\ntombolLogout\nnamaUser()"]
end
TEST -->|gunakan| POM
POM -->|bungkus| SELENIUM[Selenium WebDriver API]import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;
// Page Object untuk halaman login
public class HalamanLogin {
private final WebDriver driver;
private final WebDriverWait wait;
// @FindBy — alternatif untuk driver.findElement(By.xxx)
// Elemen ditemukan secara lazy (hanya saat diakses, bukan saat PageFactory.initElements)
@FindBy(id = "email")
private WebElement inputEmail;
@FindBy(id = "password")
private WebElement inputPassword;
@FindBy(css = "button[type='submit']")
private WebElement tombolMasuk;
@FindBy(css = ".pesan-error")
private WebElement pesanError;
@FindBy(css = ".pesan-sukses")
private WebElement pesanSukses;
public HalamanLogin(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Inisialisasi semua @FindBy field
PageFactory.initElements(driver, this);
}
// Factory method — navigasi ke halaman dan kembalikan instance
public static HalamanLogin buka(WebDriver driver) {
driver.get("https://contoh.com/login");
return new HalamanLogin(driver);
}
// Aksi atomik — satu operasi per method
public HalamanLogin isiEmail(String email) {
wait.until(ExpectedConditions.elementToBeClickable(inputEmail));
inputEmail.clear();
inputEmail.sendKeys(email);
return this; // method chaining
}
public HalamanLogin isiPassword(String password) {
inputPassword.clear();
inputPassword.sendKeys(password);
return this;
}
public HalamanDashboard klikMasuk() {
tombolMasuk.click();
// Kembalikan Page Object halaman berikutnya
return new HalamanDashboard(driver);
}
// Untuk kasus login yang gagal — tetap di halaman login
public HalamanLogin klikMasukGagal() {
tombolMasuk.click();
wait.until(ExpectedConditions.visibilityOf(pesanError));
return this;
}
// Method kombinasi — login lengkap dalam satu panggilan
public HalamanDashboard login(String email, String password) {
return isiEmail(email)
.isiPassword(password)
.klikMasuk();
}
// Getter untuk verifikasi di test
public String getPesanError() {
wait.until(ExpectedConditions.visibilityOf(pesanError));
return pesanError.getText();
}
public boolean adaPesanError() {
try {
return pesanError.isDisplayed();
} catch (org.openqa.selenium.NoSuchElementException e) {
return false;
}
}
}
// Page Object untuk halaman dashboard
public class HalamanDashboard {
private final WebDriver driver;
private final WebDriverWait wait;
@FindBy(css = "h1.judul-selamat-datang")
private WebElement judulSelamatDatang;
@FindBy(id = "nama-user")
private WebElement namaUser;
@FindBy(id = "btn-logout")
private WebElement tombolLogout;
@FindBy(css = ".menu-navigasi a")
private java.util.List<WebElement> itemMenu;
public HalamanDashboard(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
PageFactory.initElements(driver, this);
// Verifikasi bahwa kita sudah ada di halaman dashboard
wait.until(ExpectedConditions.urlContains("/dashboard"));
}
public String getNamaUser() {
wait.until(ExpectedConditions.visibilityOf(namaUser));
return namaUser.getText();
}
public String getJudul() {
return judulSelamatDatang.getText();
}
public HalamanLogin logout() {
tombolLogout.click();
return new HalamanLogin(driver);
}
public boolean sudahLogin() {
try {
return namaUser.isDisplayed();
} catch (org.openqa.selenium.NoSuchElementException e) {
return false;
}
}
}
Test yang Bersih dengan POM #
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class LoginTest extends BaseTest {
private static final String EMAIL_VALID = "[email protected]";
private static final String PASSWORD_VALID = "rahasia123";
@Test
void loginBerhasil_denganKredensialValid() {
// Arrange + Act — sangat mudah dibaca berkat POM
HalamanDashboard dashboard = HalamanLogin
.buka(driver)
.login(EMAIL_VALID, PASSWORD_VALID);
// Assert
assertThat(dashboard.sudahLogin()).isTrue();
assertThat(dashboard.getNamaUser()).contains("User");
assertThat(driver.getCurrentUrl()).contains("/dashboard");
}
@Test
void loginGagal_denganPasswordSalah() {
HalamanLogin loginPage = HalamanLogin
.buka(driver)
.isiEmail(EMAIL_VALID)
.isiPassword("password-salah")
.klikMasukGagal();
assertThat(loginPage.adaPesanError()).isTrue();
assertThat(loginPage.getPesanError()).contains("Email atau password salah");
assertThat(driver.getCurrentUrl()).contains("/login");
}
@Test
void loginGagal_denganEmailKosong() {
HalamanLogin loginPage = HalamanLogin
.buka(driver)
.isiPassword(PASSWORD_VALID)
.klikMasukGagal();
assertThat(loginPage.getPesanError()).contains("Email wajib diisi");
}
}
Actions API — Interaksi Kompleks #
Actions API digunakan untuk interaksi yang tidak bisa dilakukan dengan click() dan sendKeys() biasa.
import org.openqa.selenium.interactions.Actions;
public class ActionsDemo {
public void demo(WebDriver driver) {
Actions actions = new Actions(driver);
// Hover (mouse over) — untuk memunculkan dropdown atau tooltip
WebElement menuUtama = driver.findElement(By.id("menu-produk"));
actions.moveToElement(menuUtama).perform();
// Klik kanan (context menu)
WebElement elemen = driver.findElement(By.id("item-1"));
actions.contextClick(elemen).perform();
// Double click
actions.doubleClick(elemen).perform();
// Drag and drop
WebElement sumber = driver.findElement(By.id("item-drag"));
WebElement tujuan = driver.findElement(By.id("area-drop"));
actions.dragAndDrop(sumber, tujuan).perform();
// Drag dengan offset (geser sejumlah pixel)
actions.dragAndDropBy(sumber, 200, 0).perform();
// Tekan dan tahan tombol keyboard
WebElement inputCari = driver.findElement(By.id("input-cari"));
inputCari.sendKeys("laptop");
actions.keyDown(Keys.CONTROL)
.sendKeys("a") // Ctrl+A — select all
.keyUp(Keys.CONTROL)
.perform();
// Scroll ke elemen tertentu (Selenium 4)
WebElement elemenJauh = driver.findElement(By.id("footer"));
actions.scrollToElement(elemenJauh).perform();
// Scroll sejumlah pixel
actions.scrollByAmount(0, 500).perform(); // scroll ke bawah 500px
// Sequence aksi yang kompleks — chain multiple actions
actions.moveToElement(menuUtama)
.pause(Duration.ofMillis(500)) // tunggu dropdown muncul
.moveToElement(driver.findElement(By.linkText("Laptop")))
.click()
.perform();
}
}
JavaScript Executor #
Untuk aksi yang tidak bisa dilakukan melalui WebDriver biasa, gunakan JavaScript Executor:
import org.openqa.selenium.JavascriptExecutor;
public class JavaScriptDemo {
public void demo(WebDriver driver) {
JavascriptExecutor js = (JavascriptExecutor) driver;
// Scroll ke koordinat tertentu
js.executeScript("window.scrollTo(0, document.body.scrollHeight)");
js.executeScript("window.scrollTo(0, 0)"); // scroll ke atas
// Klik elemen yang tidak bisa diklik via WebDriver
// (tersembunyi di balik elemen lain, atau punya event listener khusus)
WebElement tombol = driver.findElement(By.id("btn-khusus"));
js.executeScript("arguments[0].click();", tombol);
// Isi input yang menolak sendKeys (misalnya input file atau readonly)
WebElement input = driver.findElement(By.id("input-tanggal"));
js.executeScript("arguments[0].value = arguments[1];", input, "2024-12-31");
// Highlight elemen untuk debugging visual
js.executeScript(
"arguments[0].style.border='3px solid red'", tombol
);
// Ambil nilai dari JavaScript
String judulHalaman = (String) js.executeScript("return document.title;");
Long tinggiHalaman = (Long) js.executeScript(
"return document.body.scrollHeight;"
);
// Hapus atribut yang memblokir interaksi (misalnya readonly)
js.executeScript("arguments[0].removeAttribute('readonly')", input);
}
}
Screenshot dan Pelaporan #
Mengambil screenshot saat test gagal sangat membantu debugging. Integrasikan ke lifecycle JUnit 5:
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.OutputType;
import org.junit.jupiter.api.extension.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// JUnit 5 Extension untuk screenshot otomatis saat test gagal
public class ScreenshotExtension implements TestWatcher {
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
// Ambil WebDriver dari test class
Object testInstance = context.getRequiredTestInstance();
if (testInstance instanceof BaseTest baseTest && baseTest.driver != null) {
ambilScreenshot(baseTest.driver, context.getDisplayName());
}
}
private void ambilScreenshot(WebDriver driver, String namaTest) {
try {
File screenshot = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
String waktu = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
String namaFile = namaTest.replaceAll("[^a-zA-Z0-9]", "_")
+ "_" + waktu + ".png";
Path tujuan = Paths.get("target", "screenshots", namaFile);
Files.createDirectories(tujuan.getParent());
Files.copy(screenshot.toPath(), tujuan);
System.out.println("Screenshot disimpan: " + tujuan);
} catch (IOException e) {
System.err.println("Gagal simpan screenshot: " + e.getMessage());
}
}
}
// Gunakan extension di BaseTest
@ExtendWith(ScreenshotExtension.class)
public class BaseTest {
public WebDriver driver; // public agar bisa diakses extension
@BeforeEach
void setUp() { driver = WebDriverFactory.buatChrome(); }
@AfterEach
void tearDown() { if (driver != null) driver.quit(); }
}
Menangani Kasus Khusus #
Alert, Popup, dan Frame #
import org.openqa.selenium.Alert;
public class KasusKhusus {
public void tanganiAlert(WebDriver driver) {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
// Tunggu alert muncul
wait.until(ExpectedConditions.alertIsPresent());
Alert alert = driver.switchTo().alert();
System.out.println("Teks alert: " + alert.getText());
// Klik OK (accept) atau Cancel (dismiss)
alert.accept(); // klik OK
// alert.dismiss(); // klik Cancel
// Alert dengan input (prompt)
alert.sendKeys("input saya");
alert.accept();
}
public void tanganiFrame(WebDriver driver) {
// Switch ke iframe berdasarkan index, name/id, atau elemen
driver.switchTo().frame(0);
driver.switchTo().frame("nama-frame");
driver.switchTo().frame(driver.findElement(By.id("iframe-konten")));
// Lakukan aksi di dalam iframe
driver.findElement(By.id("tombol-di-dalam-iframe")).click();
// Kembali ke halaman utama
driver.switchTo().defaultContent();
// Jika frame bersarang — kembali ke parent frame
driver.switchTo().parentFrame();
}
public void tanganiJendelaBartu(WebDriver driver) {
String jendelaAsal = driver.getWindowHandle();
// Klik link yang membuka tab/jendela baru
driver.findElement(By.linkText("Buka di tab baru")).click();
// Pindah ke jendela baru
for (String jendela : driver.getWindowHandles()) {
if (!jendela.equals(jendelaAsal)) {
driver.switchTo().window(jendela);
break;
}
}
// Lakukan aksi di jendela baru
System.out.println("URL jendela baru: " + driver.getCurrentUrl());
// Tutup jendela baru dan kembali ke asal
driver.close();
driver.switchTo().window(jendelaAsal);
}
public void tanganiCookies(WebDriver driver) {
// Ambil semua cookies
driver.manage().getCookies().forEach(c ->
System.out.println(c.getName() + "=" + c.getValue()));
// Tambahkan cookie (misalnya untuk skip login)
org.openqa.selenium.Cookie sessionCookie = new org.openqa.selenium.Cookie(
"session_token", "abc123xyz",
"contoh.com", "/", null
);
driver.manage().addCookie(sessionCookie);
// Hapus semua cookie (untuk reset state)
driver.manage().deleteAllCookies();
}
}
Kapan Menggunakan Selenium dan Kapan Tidak #
GUNAKAN SELENIUM JIKA:
✓ End-to-end test — verifikasi alur lengkap dari UI hingga database
✓ Regression test — pastikan fitur lama tidak rusak setelah perubahan
✓ Web scraping — halaman yang membutuhkan JavaScript untuk merender konten
✓ Test lintas browser — verifikasi di Chrome, Firefox, Edge, Safari
✓ Test form kompleks dengan validasi, file upload, dropdown dinamis
✓ Interaksi yang membutuhkan browser sungguhan (WebRTC, canvas, WebGL)
PERTIMBANGKAN ALTERNATIF JIKA:
✗ Unit test atau integration test murni → tidak butuh browser, gunakan JUnit + Mockito
✗ API testing → REST Assured atau Postman lebih tepat sasaran
✗ Test performa / load testing → JMeter atau k6 lebih cocok
✗ Halaman yang bisa di-parse tanpa JavaScript → Jsoup jauh lebih cepat untuk scraping
✗ Test sangat cepat dan banyak → Selenium lambat; pertimbangkan Playwright atau Cypress
✗ Test mobile native → Appium (berbasis Selenium) untuk Android/iOS
Ringkasan #
- Selenium Manager (bawaan Selenium 4) otomatis mengelola driver — tidak perlu lagi mengunduh
chromedriveratau menggunakan WebDriverManager pihak ketiga.- Selalu tutup driver dengan
quit()di@AfterEach—quit()menutup semua jendela dan membunuh proses driver, sedangkanclose()hanya menutup tab aktif.- Prioritas locator: ID > data-testid > CSS selector > XPath relatif. Hindari XPath absolut dan class yang dibuat otomatis oleh framework CSS — keduanya sangat rapuh.
- Explicit wait (
WebDriverWait) wajib digunakan, bukanThread.sleep(). Tunggu kondisi spesifik (elementToBeClickable,visibilityOf,urlContains) bukan waktu tetap yang tidak akurat.- Page Object Model adalah pola wajib untuk test yang bisa dimaintain — satu kelas per halaman, satu method per aksi, locator tidak tersebar di test class.
ActionsAPI untuk interaksi kompleks: hover, double click, drag and drop, key combinations, dan scroll.JavascriptExecutorsebagai last resort untuk aksi yang tidak bisa dilakukan via WebDriver.- Ambil screenshot otomatis saat test gagal menggunakan JUnit 5
TestWatcher— sangat membantu debugging tanpa harus mereproduksi kegagalan secara manual.- Selenium paling cocok untuk end-to-end test dan web scraping yang butuh JavaScript — untuk unit test dan API test, gunakan tool yang lebih ringan dan tepat sasaran.
← Sebelumnya: Hibernate Berikutnya: Artikel dan Sumber Daya →