Repository navigation
How to use testclient with selenium in pytest? #3507
|
Hello!! I have done a decent bit of searching around and can't quite find an answer already, hopefully this is not too obvious to those more familiar with the package. I am trying to test a fastapi app (that uses the starlette TestClient, which is ultimately an httpx client) using selenium, and can't quite figure out how to forward the webdriver tests to the |
Replies: 2 comments
|
ASGI (FastAPI/Starlette): import socket, threading, time
import pytest, uvicorn
@pytest.fixture(scope="session")
def live_server():
with socket.socket() as s: # free port from the OS
s.bind(("127.0.0.1", 0))
port = s.getsockname()[1]
server = uvicorn.Server(uvicorn.Config(app, host="127.0.0.1", port=port, log_level="error"))
thread = threading.Thread(target=server.run, daemon=True)
thread.start()
deadline = time.monotonic() + 10
while not server.started: # wait until it is really listening
if not thread.is_alive() or time.monotonic() > deadline:
raise RuntimeError("test server failed to start")
time.sleep(0.01)
yield f"http://127.0.0.1:{port}"
server.should_exit = True
thread.join(timeout=5)WSGI (e.g. Flask): Then use |
|
The live-server approach in the earlier reply is correct. One fixture detail is worth changing: keep the OS-assigned socket open and pass that socket to Uvicorn. Closing it after finding the port leaves a race before the server binds it, especially in parallel test runs. import socket
import threading
import time
import pytest
import uvicorn
@pytest.fixture(scope='session')
def live_server():
# Import your FastAPI app here.
from myapp import app
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(('127.0.0.1', 0))
port = sock.getsockname()[1]
server = uvicorn.Server(uvicorn.Config(app, log_level='error'))
thread = threading.Thread(
target=server.run, kwargs={'sockets': [sock]}, daemon=True
)
thread.start()
try:
deadline = time.monotonic() + 10
while not server.started:
if not thread.is_alive() or time.monotonic() >= deadline:
raise RuntimeError('Test server did not start')
time.sleep(0.01)
yield f'http://127.0.0.1:{port}'
finally:
server.should_exit = True
thread.join(timeout=5)Then use This address is suitable for a browser on the same machine/network namespace. A remote Selenium container needs an address reachable from that container. Also, TestClient dependency overrides, cookies, and transactions do not automatically transfer to an independently started app process. |
TestClient(and httpx'sASGITransport/WSGITransport) call your app in-process; its base URLhttp://testserverdoesn't exist on the network. A Selenium-driven browser is a separate process that only speaks real TCP/HTTP, so its requests can't be routed through that transport. The usual approach is a "live server" fixture: run the app on a real port in a background thread and point the driver at it.ASGI (FastAPI/Starlette):