This episode covers combining UI tests with API checks, validating backend responses and UI state, testing end-to-end flows from the UI to the service layer, and using API helpers to speed up UI setup in a Selenium suite.

UI tests only see the surface. When you assert that text appears on a page, you don't know whether that data comes from the right backend or from a hidden mock. Episode 16 connects two worlds: UI testing with Selenium and API validation — so tests prove that the whole stack works together.
This approach yields two major benefits. First, more complete coverage: service layer errors that don't clearly reflect in the UI are still caught. Second, efficiency: much slow UI setup can be replaced with direct API calls, making the suite far faster.
Combining UI and API makes sense when the UI displays data that comes from the backend. Instead of only asserting page text, also validate the raw API data that feeds that text:
import requests
halaman = DashboardPage(driver)
driver.get("https://app.example.com/dashboard")
assert "Pendapatan" in halaman.ambil_tabel()
resp = requests.get("https://app.example.com/api/dashboard", timeout=5)
resp.raise_for_status()
data = resp.json()
assert data["status"] == "ok"
assert len(data["items"]) == len(halaman.baris_tabel())resp.json() validates that the API responds correctly, and len(data["items"]) == len(halaman.baris_tabel()) proves that the item count in the API matches what the UI renders. Together, these two validations are far stronger than either alone.
An important pattern is validating that what the UI shows truly comes from the data the backend sends:
data = requests.get("https://app.example.com/api/profil", timeout=5).json()
nama_ui = driver.find_element("id", "nama-pengguna").text
assert nama_ui == data["nama"], f"UI menampilkan {nama_ui}, API mengirim {data['nama']}"assert nama_ui == data["nama"] unifies two sources: the display and the data. A mismatch between them indicates a bug in the rendering or mapping layer — a bug never visible if the test only checks one side.
Failed APIs must be displayed correctly by the UI. Simulate an error condition with an invalid API call:
resp = requests.get(
"https://app.example.com/api/item/999999",
timeout=5,
)
assert resp.status_code == 404
assert "Tidak ditemukan" in driver.find_element("id", "pesan-error").textresp.status_code == 404 ensures the backend answers correctly, and the second assertion ensures the UI displays the right message. Tests like this protect the user experience when the service layer fails.
For flows like ordering, validate the side effects at the service layer after a UI action:
halaman = CheckoutPage(driver)
halaman.lanjutkan_pembayaran()
# Verifikasi di service layer
pesanan = requests.get(
f"https://app.example.com/api/pesanan/{id_pesanan}",
timeout=5,
).json()
assert pesanan["status"] == "PAID"
assert "Terima kasih" in driver.find_element("id", "konfirmasi").textAfter the UI action completes, pesanan["status"] == "PAID" proves the transaction is actually recorded at the service layer — not just a changed display. This combination gives full confidence in the end-to-end flow.
Slow UI setup — especially login and data preparation — can be sped up via the API. The best way: grab the cookie from an API session and use it in Selenium:
import requests
from selenium import webdriver
s = requests.Session()
s.post(
"https://app.example.com/api/login",
json={"email": os.environ["TEST_EMAIL"], "password": os.environ["TEST_PASSWORD"]},
timeout=5,
)
driver = webdriver.Chrome()
driver.get("https://app.example.com/login")
for cookie in s.cookies:
driver.add_cookie({"name": cookie.name, "value": cookie.value})
driver.get("https://app.example.com/dashboard")s.post("https://app.example.com/api/login", json={...}) creates an authenticated session via the API. The session's cookies are injected into Selenium with driver.add_cookie(...), so tests skip the slow UI login form and go straight to the page under focus.
The same principle applies to data: create users, orders, or documents via the API instead of filling forms. A test needing ten cart items just calls the API three times instead of clicking the UI thirty times. This speed is an investment you feel directly in suite duration.
Warning
Session cookies expire. Keep a healthy API session for each test run, and never write tokens or cookies to logs — returning to the security habits from episode 12.
Episode 16 connects two worlds often kept apart: UI and backend. You can now combine UI assertions with API validation, check consistency between display and data, prove side effects at the service layer, and speed up the suite with API-based setup.
Key takeaways:
In episode 17 next, we'll cover mobile web and hybrid testing — automating mobile browsers and testing responsive design, using emulation mode and real devices, Appium integration for hybrid applications, and mobile-specific locator and gesture strategies.