Resource library

QA How-To

Selenium BiDi vs CDP for Network Testing (2026)

Compare Selenium BiDi vs CDP for network testing, with runnable Java and Python examples, browser support, migration advice, and a clear 2026 verdict.

20 min read | 2,710 words

TL;DR

Use WebDriver BiDi as the default for new Selenium network tests because it is standards-based and designed for multiple browsers. Use CDP when a Chromium-only test needs a mature DevTools domain that BiDi does not yet provide, then keep that dependency isolated.

Key Takeaways

  • Choose WebDriver BiDi for new cross-browser network tests when its current commands cover the requirement.
  • Keep CDP for Chromium-only capabilities that WebDriver BiDi has not standardized or exposed yet.
  • Prefer Selenium's high-level network APIs over low-level protocol classes whenever possible.
  • Enable BiDi before creating the driver, and register listeners before navigation starts.
  • Use concurrent collections or explicit waits because network callbacks do not run like ordinary test statements.
  • Isolate protocol-specific code behind a small adapter so browser and Selenium upgrades remain manageable.

Selenium BiDi vs CDP for network testing is no longer a choice between a future idea and the only practical tool. In 2026, WebDriver BiDi is the preferred direction for portable event-driven automation, while Chrome DevTools Protocol remains useful for deep Chromium-specific network control. Start new tests with BiDi when the required feature exists; retain CDP only where its broader DevTools surface is essential.

The important distinction is architectural. WebDriver commands are request-response operations, but network traffic is asynchronous. BiDi adds a persistent two-way channel standardized for browser automation. CDP also streams events and accepts commands, but it is Chrome's debugging protocol, not a cross-browser WebDriver standard. This guide turns that distinction into an engineering decision with runnable examples.

TL;DR

Decision factor WebDriver BiDi CDP through Selenium
Governance W3C browser-automation standard Chromium DevTools protocol
Browser goal Chrome, Edge, Firefox, and other conforming browsers Chromium family
Selenium direction Preferred for new event-driven automation Temporary or specialized bridge
Network events Standard request, response, auth, and failure events Broad, detailed Network and Fetch domains
Version coupling Selenium binding plus browser implementation maturity Often tied to a generated DevTools version package
Best fit Portable request observation and supported interception Chromium-only diagnostics and protocol features absent from BiDi
Main risk Coverage still varies by binding and browser Browser-version churn and vendor lock-in

The practical verdict is simple: select capability first, portability second, and protocol depth third. If both interfaces satisfy the assertion, BiDi wins. If only CDP exposes the required Chromium detail, use CDP deliberately and mark the test Chromium-only.

1. Selenium BiDi vs CDP for Network Testing: The Core Difference

WebDriver BiDi extends the WebDriver model with bidirectional messaging. After the session negotiates a WebSocket endpoint, the client can subscribe to events such as network.beforeRequestSent, network.responseStarted, network.responseCompleted, and network.fetchError. The browser can publish those events without waiting for the next classic WebDriver command. BiDi also defines commands for interception, continuation, authentication, cache behavior, and response provision as implementations mature.

CDP connects to Chromium's debugging machinery. Selenium exposes it through HasDevTools, generated version-specific domain classes, and higher-level helpers such as NetworkInterceptor. CDP covers far more than standardized browser automation, including detailed performance, security, emulation, tracing, and browser internals. That breadth is valuable, but domain schemas can change with Chromium releases.

Do not confuse transport with test purpose. Both can observe traffic, block or continue selected requests, and support network-aware assertions. The choice concerns compatibility and maintenance. BiDi promises the same semantic command across conforming browsers. CDP describes what Chromium currently implements. For a broader Selenium foundation, review the Selenium BiDi automation complete guide before adding protocol-specific abstractions.

2. What You Will Build

You will create small tests that demonstrate the decision boundary rather than a toy API call:

  • a Python BiDi test that captures requests and proves a target URL was observed;
  • a Python BiDi test that blocks a matching request and verifies the failure event;
  • a Java CDP-backed interceptor that returns deterministic HTML without calling the server;
  • a Java CDP event listener that captures response status codes;
  • a protocol-neutral checklist for choosing and migrating production tests.

Each example uses a public Selenium test page or a data URL, explicit assertions, and cleanup. Network tests are most useful when they establish an observable contract, such as a checkout page sending one POST to /orders or an analytics request failing without breaking navigation. Merely printing traffic produces logs, not a test.

3. Prerequisites

Use Python 3.11 or newer for the BiDi examples, Java 17 or newer for CDP, Selenium 4.43.0, pytest 8, and JUnit Jupiter 5.13. Selenium Manager can resolve a compatible local driver when Chrome or Firefox is installed. Put the Python dependencies in requirements.txt:

selenium==4.43.0
pytest==8.4.1

Install and verify them:

python -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
python -c "import selenium; print(selenium.__version__)"

Expected output begins with 4.43.0. On Windows PowerShell, activate with .venv\Scripts\Activate.ps1.

For Java, create a Maven project and add Selenium plus JUnit. The selenium-devtools-v150 artifact matches the generated CDP package used below. A different installed Chromium major may require the corresponding artifact and import package.

<dependencies>
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.43.0</version>
  </dependency>
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-devtools-v150</artifactId>
    <version>4.43.0</version>
  </dependency>
  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.13.4</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Verify resolution with mvn -q dependency:tree. The output should contain selenium-java and selenium-devtools-v150. If your browser major differs, do not blindly copy the generated package. Align the artifact first.

4. Step 1: Enable a WebDriver BiDi Session

Python's high-level network API obtains the BiDi connection when the driver is created with WebSocket support. Enable it through browser options before constructing the session. Save this reusable fixture as conftest.py:

import pytest
from selenium import webdriver

@pytest.fixture(params=["chrome", "firefox"])
def bidi_driver(request):
    if request.param == "chrome":
        options = webdriver.ChromeOptions()
        options.enable_bidi = True
        driver = webdriver.Chrome(options=options)
    else:
        options = webdriver.FirefoxOptions()
        options.enable_bidi = True
        driver = webdriver.Firefox(options=options)

    yield driver
    driver.quit()

The parameterized fixture exposes browser differences immediately. If your CI currently installs only one browser, reduce params temporarily, but keep the test code browser-neutral. The quit() call matters because it closes both the WebDriver session and associated event connection.

Verify discovery before adding network logic:

pytest --collect-only -q

After creating at least one test using bidi_driver, pytest should list Chrome and Firefox cases. A session-creation failure here is an environment problem, not a network assertion failure. Resolve browser availability, driver resolution, and remote Grid capability forwarding first.

5. Step 2: Capture Requests with BiDi

Create test_bidi_network.py. Register the handler before navigation so the initial document request cannot race past the subscription. Selenium's Python Network wrapper passes a Request object to the callback.

from selenium.webdriver.support.ui import WebDriverWait

def test_bidi_captures_document_request(bidi_driver):
    seen_urls = []
    network = bidi_driver.network
    handler_id = network.add_request_handler(
        "before_request",
        lambda request: seen_urls.append(request.url),
    )

    try:
        target = "https://www.selenium.dev/selenium/web/blank.html"
        bidi_driver.get(target)
        WebDriverWait(bidi_driver, 10).until(
            lambda _driver: any(url == target for url in seen_urls)
        )
        assert target in seen_urls
    finally:
        network.remove_request_handler("before_request", handler_id)

This callback records only the value needed by the assertion. Production suites should filter early and avoid storing every image, font, and telemetry request. The explicit wait handles event delivery without using a fragile sleep. Removing the handler prevents duplicate callbacks when a driver fixture has broader scope.

Run and verify the two browser cases:

pytest -q test_bidi_network.py::test_bidi_captures_document_request

Expected result is two passing cases when both browsers are installed. For a deeper Java treatment, see Selenium BiDi network testing in Java. Python teams can use the matching Selenium BiDi network Python guide.

6. Step 3: Block a Request with BiDi

Interception differs from passive observation. A request handler installs an intercept for its phase. The callback must then continue, modify, or fail the paused request. Limit the URL pattern so unrelated page resources are not paused.

from selenium.webdriver.support.ui import WebDriverWait

def test_bidi_blocks_selected_request(bidi_driver):
    failed_urls = []
    network = bidi_driver.network

    block_id = network.add_request_handler(
        "before_request",
        lambda request: request.fail_request(),
        url_patterns=[{"type": "string", "pattern": "*blocked.png"}],
    )
    error_id = network.add_request_handler(
        "fetch_error",
        lambda request: failed_urls.append(request.url),
    )

    try:
        html = "<img src='https://example.test/blocked.png'>"
        bidi_driver.get("data:text/html," + html)
        WebDriverWait(bidi_driver, 10).until(
            lambda _driver: any(url.endswith("blocked.png") for url in failed_urls)
        )
        assert len([u for u in failed_urls if u.endswith("blocked.png")]) == 1
    finally:
        network.remove_request_handler("before_request", block_id)
        network.remove_request_handler("fetch_error", error_id)

The assertion proves browser-observed failure, not just callback execution. URL-pattern support can vary while browser implementations converge, so run this test against every CI browser you claim to support. For a focused walkthrough of the same capability, use intercepting Selenium BiDi requests in Python.

Verify it independently:

pytest -q test_bidi_network.py::test_bidi_blocks_selected_request

A pass means the selected image was canceled and a network.fetchError event arrived. If navigation hangs, the request was probably intercepted without being failed or continued.

7. Step 4: Stub a Response with Selenium's CDP Helper

Selenium's Java NetworkInterceptor is a high-level helper implemented for drivers that expose DevTools. It avoids importing generated Network domain commands for a common stubbing case. Create src/test/java/example/CdpStubTest.java:

package example;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.openqa.selenium.remote.http.Contents.utf8String;

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.devtools.NetworkInterceptor;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.http.Route;

class CdpStubTest {
  @Test
  void returnsDeterministicHtml() {
    WebDriver driver = new ChromeDriver();
    Route route = Route.matching(
        request -> request.getUri().endsWith("/stubbed-page"))
      .to(() -> request -> new HttpResponse()
        .setStatus(200)
        .addHeader("Content-Type", "text/html; charset=utf-8")
        .setContent(utf8String("<h1 id='result'>stubbed</h1>")));

    try (NetworkInterceptor ignored = new NetworkInterceptor(driver, route)) {
      driver.get("https://example.test/stubbed-page");
      assertEquals("stubbed", driver.findElement(By.id("result")).getText());
    } finally {
      driver.quit();
    }
  }
}

The browser never needs DNS for the matched URL because the interceptor supplies the response. Scope the route narrowly. A catch-all route can accidentally replace scripts, CSS, redirects, and authentication requests, making the page behavior meaningless. Try-with-resources guarantees that interception stops before later tests reuse shared infrastructure.

Verify with mvn -q -Dtest=CdpStubTest test. Maven should report one passing test. Although this helper is convenient, its driver requirement remains DevTools-based, so treat the example as Chromium-specific.

8. Step 5: Listen to CDP Response Events

Use a version-specific CDP domain when you need fields not exposed by a Selenium helper. The following test collects HTTP status codes. The generated v150 import is intentionally visible because that version coupling is part of the trade-off.

package example;

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

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.devtools.v150.network.Network;

class CdpEventsTest {
  @Test
  void capturesSuccessfulResponse() {
    WebDriver driver = new ChromeDriver();
    DevTools tools = ((HasDevTools) driver).getDevTools();
    List<Integer> statuses = new CopyOnWriteArrayList<>();

    try {
      tools.createSession();
      tools.send(Network.enable(
        Optional.empty(), Optional.empty(), Optional.empty(),
        Optional.empty(), Optional.empty()));
      tools.addListener(Network.responseReceived(), event ->
        statuses.add(event.getResponse().getStatus()));

      driver.get("https://www.selenium.dev/selenium/web/blank.html");
      assertTrue(statuses.stream().anyMatch(code -> code >= 200 && code < 400));
    } finally {
      tools.close();
      driver.quit();
    }
  }
}

CopyOnWriteArrayList protects the collection from concurrent event delivery while the test thread reads it. For a real API assertion, filter by URL and method, then assert the exact status expected for that resource. Do not assume the first response is the main document because redirects, service workers, and preload traffic can alter order.

Run mvn -q -Dtest=CdpEventsTest test. If Selenium warns that it cannot find a matching CDP implementation, align the browser, Selenium version, and selenium-devtools-vNNN artifact. That maintenance burden is a concrete reason to prefer BiDi when feature coverage is equal.

9. Selenium BiDi vs CDP for Network Testing by Use Case

Use case matters more than raw protocol size. For request observation across Firefox and Chromium, BiDi is the direct choice. It expresses automation-level events without requiring generated Chrome domain imports. For basic authentication, supported request modification, blocking, and response lifecycle assertions, use Selenium's high-level BiDi network surface.

CDP remains justified for Chrome-specific investigations involving DevTools fields or domains that lack a BiDi equivalent. Examples include a test that validates a Chromium experiment, obtains specialized cache details, or correlates low-level performance information. Those tests should carry an explicit browser tag such as @Tag("chromium"), separate dependency management, and a clear owner.

Network throttling deserves special care. Do not claim a portable slow-network test merely because a CDP emulation command works in Chrome. BiDi network conditions are evolving, and supported behavior must be verified per browser. The dedicated Selenium BiDi network conditions tutorial covers that narrower problem. Also distinguish browser network interception from direct API testing. If the requirement is to validate an endpoint independently of UI behavior, an HTTP client is faster, clearer, and easier to diagnose.

10. Reliability, Security, and Test Design

Network callbacks are asynchronous, so protect shared state and wait for a business-specific condition. In Java, use concurrent collections, atomics, latches, or Selenium waits. In Python, keep callbacks small and use WebDriverWait on a thread-safe observable result. Never add a fixed five-second sleep and assume all traffic has settled. Modern pages maintain WebSockets, poll indefinitely, and send beacons after user actions.

Sanitize diagnostics. Headers and bodies may contain bearer tokens, session cookies, personal data, signed URLs, or payment details. Log the method, normalized route, status, and duration when those fields are enough. Redact Authorization, Cookie, Set-Cookie, and organization-specific secrets before attaching traffic to CI artifacts.

Assert contracts rather than incidental noise. A robust test can require one POST /orders with an expected content type and a 201 response. A brittle test requires exactly 47 total requests in a fixed order. Third-party tags, browser preloads, caching, and application releases make total-count assertions unstable. Keep UI assertions too: a successful network response does not prove the user saw the expected state.

11. Migration from CDP to BiDi

Inventory CDP usage by domain and behavior, not file count. Classify every command as observation, interception, emulation, performance diagnostics, or browser administration. Map the simple network cases first. A Network.responseReceived listener usually maps conceptually to a BiDi response event, while a specialized Chromium performance command may have no portable replacement.

Wrap the protocol at the assertion boundary. For example, expose waitForResponse(Predicate<ResponseSummary>) from your test utility instead of returning CDP event objects. Keep url, method, status, and selected headers in your own immutable record. A BiDi implementation can then populate the same record without forcing changes through every test.

Run old and new collectors together for a short validation window, but let only one drive the assertion. Compare normalized results in logs across Chrome, Edge, and Firefox. Once event filtering and timing agree, remove the CDP path for that capability. Do not translate generated CDP types one-for-one into low-level BiDi types. Selenium's 2026 guidance favors its high-level network and script APIs because low-level protocol mapping classes are internal implementation surfaces.

12. Which Should You Choose

Choose BiDi when you are writing a new test, need Firefox plus Chromium coverage, and the high-level Selenium network API exposes the event or interception you need. This is the maintainable default. It aligns the test with WebDriver's cross-browser contract and reduces generated Chrome-version imports.

Choose CDP when the requirement is explicitly Chromium-only or depends on a DevTools feature absent from BiDi. Record that exception in code review: name the missing capability, supported browser majors, dependency package, and fallback behavior. Prefer NetworkInterceptor or another Selenium abstraction before direct versioned domain calls.

Choose neither for ordinary service contract testing. Send requests through a dedicated API client, validate schemas and status codes there, and reserve browser network hooks for questions only a browser can answer: did this user action initiate the call, did browser credentials apply, did a service worker alter it, or did the UI handle a failed dependency? You can practice broader automation decisions in the QA testing practice workspace and compare your resume evidence in QAJobFit Resume Studio.

13. Common Mistakes

  • Registering after navigation: the document and early subresources may already be complete. Subscribe first.
  • Pausing without continuing: intercepted requests remain blocked until you continue, fail, authenticate, or provide a response.
  • Treating CDP as cross-browser: Edge shares Chromium foundations, but Firefox does not implement Chrome's protocol surface.
  • Importing a random CDP version: generated packages must align with Selenium and the installed Chromium major.
  • Capturing everything: unbounded lists increase memory use and make assertions noisy. Filter by URL, method, resource type, or browsing context.
  • Asserting event order: parallel loading, caching, redirects, and service workers can reorder traffic. Correlate by request ID or stable route.
  • Leaking handlers: remove subscriptions and close interceptors so later tests do not receive duplicate events.
  • Logging credentials: redact secrets before publishing build artifacts.
  • Using network success as the only oracle: assert the visible user result as well as the backend exchange.
  • Depending on low-level BiDi classes by default: use Selenium's high-level network APIs and treat internals as a documented, isolated escape hatch.

14. Troubleshooting

No events arrive -> Confirm BiDi was enabled in options before driver creation, register the callback before navigation, and verify the remote node forwards the WebSocket capability.

The browser hangs after adding an intercept -> Every paused request needs an action. Continue non-target requests or use a narrow URL pattern, and always fail or continue the target.

Chrome reports no matching CDP implementation -> Align Selenium, the installed browser major, and the selenium-devtools-vNNN dependency. Remove stale DevTools artifacts from the dependency tree.

A callback updates a list but the assertion sees nothing -> Use a concurrent collection and an explicit wait. Event callbacks and the test statement can execute on different threads.

The test passes locally but fails on Grid -> Check browser versions on nodes, BiDi capability negotiation, proxy rules, and whether the Grid version supports forwarding the event channel. Capture capabilities without recording secrets.

Request totals vary between runs -> Filter to the application route that represents the contract. Disable or account for cache, service workers, retries, and third-party traffic instead of asserting a global count.

Interview Questions and Answers

Interviewers usually test whether you can separate standards, implementation maturity, and test design. The model answers in the structured section below cover protocol selection, event timing, browser scope, version coupling, interception safety, and migration. A strong answer starts with the requirement and then explains why a protocol fits, rather than declaring one technology universally superior.

15. Where To Go Next

Start by converting one passive CDP response listener to BiDi, run it on Chrome and Firefox, and compare normalized results. Then try one targeted interception. Keep any unmatched CDP command behind a small adapter with a Chromium-only tag.

For specialized follow-ups, learn BiDi authentication handling in Java, examine Selenium BiDi JavaScript error capture, and compare Selenium BiDi with Playwright network interception. These exercises reveal which differences come from protocol design and which come from library ergonomics.

Conclusion

For Selenium BiDi vs CDP for network testing in 2026, BiDi is the default for portable, standards-based automation, while CDP is the deliberate exception for deeper Chromium-only capabilities. The best suite does not choose by novelty. It uses the narrowest stable interface that proves a user-facing contract.

Build one filtered listener, assert a meaningful request or response, and execute it across your supported browsers. That small experiment will show whether BiDi already covers your requirement and exactly where a remaining CDP dependency belongs.

Interview Questions and Answers

What is the main difference between WebDriver BiDi and CDP for network testing?

WebDriver BiDi is a browser-automation standard designed for portable bidirectional commands and events. CDP is Chromium's debugging protocol and exposes a broader, vendor-specific surface. I choose BiDi when its feature covers the assertion and CDP only for a documented Chromium-specific gap.

Why must a network listener be registered before navigation?

The main document and early resources can be requested immediately after navigation begins. Registering afterward creates a race and can miss the exact event under test. I subscribe first, perform the action, wait for a filtered event, and remove the listener in cleanup.

How would you make an asynchronous network assertion reliable?

I filter by stable attributes such as route and method, write callback results into thread-safe state, and use an explicit wait for the expected condition. I correlate related events by request identifier when available. I never rely on a fixed sleep or total traffic count.

When is CDP still the better choice?

CDP is appropriate when a test is intentionally Chromium-only and needs a DevTools command or field that BiDi has not standardized or Selenium has not exposed. I isolate generated CDP types, pin compatible versions, and tag the browser constraint so the limitation is visible.

What happens if an intercepted request is not continued or failed?

The browser leaves the request paused, which can hang page loading or the user action. Every interception callback needs a terminal decision: continue, modify and continue, fail, authenticate, or provide a response. Narrow URL patterns reduce the chance of pausing unrelated resources.

How would you migrate a large CDP test suite to BiDi?

I inventory behavior by capability, then migrate ordinary request and response observation first. Tests consume a protocol-neutral response summary rather than CDP event classes. I compare CDP and BiDi output during a short validation period and retain isolated CDP adapters only for confirmed gaps.

What security risks exist when capturing browser network traffic?

Traffic can expose authorization headers, cookies, personal data, signed URLs, and request bodies. I collect only fields required by the assertion and redact secrets before logging or attaching artifacts. Access to raw captures should follow the same controls as production-like test data.

Frequently Asked Questions

Is WebDriver BiDi replacing CDP in Selenium?

BiDi is replacing many CDP-based automation use cases as standardized features become available, but it does not duplicate every DevTools domain. Keep CDP for explicit Chromium-only gaps and prefer BiDi for supported cross-browser behavior.

Can Selenium BiDi capture network requests in Firefox?

Yes, Firefox supports WebDriver BiDi network capabilities, subject to the browser and Selenium binding versions in use. Run each listener and interception scenario in CI because implementation coverage can differ at feature level.

Does CDP network interception work in every Selenium browser?

No. CDP is a Chromium protocol, so Selenium CDP integrations target Chrome-family browsers. It should not be presented as a portable Firefox solution.

Should I use Selenium network events instead of an API client?

Use browser events when the assertion concerns a browser-triggered request, credentials, service workers, or UI handling. Use a direct HTTP client for endpoint contracts, schemas, and broad API coverage because it is simpler and faster.

Why does a CDP version mismatch happen?

Selenium generates Java bindings for selected Chromium protocol versions, while installed browsers update independently. Align the browser major, Selenium release, and matching selenium-devtools artifact to prevent missing implementation warnings.

How do I avoid flaky Selenium network assertions?

Subscribe before the action, filter to a stable route, store callback data safely, and wait for a precise condition. Avoid sleeps, global request counts, and assumptions about response order.

Are Selenium's low-level BiDi classes safe to use?

They are available as an escape hatch, but Selenium marks low-level protocol implementation APIs as internal. Prefer the high-level network API and isolate any unavoidable low-level dependency behind your own adapter.

Related Guides