Learn Selenium - API & Backend Validation with Selenium
Episode 16 of 23

Learn Selenium - API & Backend Validation with Selenium

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

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 Tests with API Checks

When to Combine

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:

PythonValidating UI and API together
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.

Validating Backend Responses and UI State

Checking Consistency

An important pattern is validating that what the UI shows truly comes from the data the backend sends:

PythonUI state matches backend data
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.

Validating Error Handling

Failed APIs must be displayed correctly by the UI. Simulate an error condition with an invalid API call:

PythonAPI error and UI response
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").text

resp.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.

E2E Flow from UI to the Service Layer

Proving Side Effects

For flows like ordering, validate the side effects at the service layer after a UI action:

PythonE2E with service layer verification
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").text

After 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.

Speeding up Setup with API Helpers

Login via API, Continue with UI

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:

PythonInject cookie from an API session
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.

Data Setup via API

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.

Conclusion

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:

  • Validate UI and API together for complete stack coverage.
  • Check consistency between API data and what the UI renders.
  • Verify transaction side effects at the service layer, not just the display.
  • Use API sessions for login and data setup to keep the suite fast.
  • Never write tokens or cookies to logs.

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.

Learn Selenium - API & Backend Validation with Selenium | Learn Selenium