Unser Standpunkt, ganz klar formuliert: Wir sind Geonode und verkaufen Proxys, und Playwright wird häufig über diese Proxys ausgeführt, um Daten zu scrapen und geografische Tests durchzuführen. Ehrlich gesagt hat die überwiegende Mehrheit der Playwright-Timeouts nichts mit Proxys zu tun. Ein Selektor, der nichts findet, ein Element, das von einem Cookie-Banner verdeckt wird, eine Animation, die nie zum Stillstand kommt – all das führt zum gleichen Fehler, egal ob Ihr Datenverkehr direkt oder über sechs Zwischenstationen läuft. Es gibt einen echten, proxybezogenen Fall, der gegen Ende behandelt wird: Privatanbinder-Verbindungen verursachen echte Latenz, sodass auf lokale Tests abgestimmte Standardeinstellungen zu falschen Fehlern führen. Überprüfen Sie jedoch zuerst den Selektor. Wenn Ihr Test ohne konfigurierten Proxy auf identische Weise fehlschlägt, ist der Proxy nicht das Problem.
Die sechs Timeouts und ihre Standardwerte
Zunächst muss man verstehen, dass es sich hierbei um separate Mechanismen mit unterschiedlichen Standardwerten handelt. Wenn man weiß, welcher davon ausgelöst wurde, weiß man auch, wo man nachsehen muss.
| Timeout | Standardwert | Einstellung über |
|---|---|---|
| Test | 30.000 ms | testConfig.timeout, test.setTimeout() |
| Expect | 5.000 ms | testConfig.expect.timeout, Option pro Assertion |
| Action | Kein Timeout | testOptions.actionTimeout, Option pro Aufruf |
| Navigation | Kein Timeout | testOptions.navigationTimeout, Option pro Aufruf |
| „beforeAll“-/„afterAll“-Hook | 30.000 ms | test.setTimeout() innerhalb des Hooks |
| Global | Keine | testConfig.globalTimeout |
Werte aus der Playwright-Dokumentation zu Timeouts.
Zwei davon überraschen viele.
„Action“ und „Navigation“ haben standardmäßig kein Timeout. Sie unterliegen lediglich dem Test-Timeout. Ein nicht qualifiziertes „page.click()“ wartet also so lange, bis das verbleibende Testbudget aufgebraucht ist, und die angezeigte Fehlermeldung bezieht sich darauf, dass der Test das Timeout erreicht hat, nicht der Klick. Deshalb lautet die Meldung „30000 ms“, obwohl niemand einen 30-Sekunden-Klick konfiguriert hat.
Das globale Timeout hat überhaupt keinen Standardwert. In der Dokumentation wird sein Zweck damit beschrieben, „eine übermäßige Ressourcenauslastung zu verhindern, wenn alles schiefgelaufen ist“ – es lohnt sich, dies in der CI einzustellen, damit eine hängengebliebene Testsuite fehlschlägt, anstatt einen Runner auf unbestimmte Zeit zu blockieren.
Was „Timeout von 30000 ms überschritten“ eigentlich bedeutet
Genau diese Meldung bezieht sich auf das Test-Timeout, und das Test-Timeout ist eher ein Zeitlimit als eine Diagnose. Etwas innerhalb dieses Zeitraums hat zu lange gedauert, und die Meldung nennt das Zeitlimit, nicht den Verursacher.
Geordnet nach der Häufigkeit, mit der die jeweiligen Ursachen tatsächlich vorliegen:
1. Ein Locator hat keine Übereinstimmung gefunden. Der Selektor ist falsch, oder das Element ist noch nicht erschienen, oder es befindet sich in einem iframe oder einer Shadow Root, die Sie nicht berücksichtigt haben. Playwright wartet geduldig auf etwas, das niemals existieren wird.
2. Das Element existiert, ist aber nicht interaktiv. Es wird von einem Overlay, einem Cookie-Banner oder einem Sticky-Header verdeckt. Es ist deaktiviert. Es wird noch animiert. Playwright wartet darauf, dass es anklickbar wird, was jedoch nie geschieht.
3. Eine Navigation wurde nie abgeschlossen. Eine hängende Netzwerkanfrage, eine Weiterleitungsschleife oder eine „waitUntil“-Bedingung – insbesondere „networkidle“ –, die eine Seite mit persistenten Verbindungen niemals erfüllen wird.
4. Eine Assertion wurde nie wahr. Ein „expect“, das auf eine Bedingung abfragt, die die Anwendung nicht erreicht.
5. Der Test macht tatsächlich zu viel. Real und am seltensten.
Die Reihenfolge ist entscheidend, da die Behebung völlig unterschiedlich ausfällt. Nur Fall 5 lässt sich durch eine Erhöhung des Timeouts beheben. In den anderen vier Fällen bedeutet eine Erhöhung lediglich, dass man länger auf denselben Fehler wartet.
Die Überprüfbarkeit ist der Grund, warum Ihr Klick auf sich warten lässt
Wenn man dies versteht, klärt sich ein Großteil der Verwirrung, da dadurch erklärt wird, was Playwright während dieser dreißig Sekunden tut.
In der Dokumentation zur Ausführbarkeit heißt es, dass Playwright „vor der Ausführung von Aktionen eine Reihe von Ausführbarkeitsprüfungen an den Elementen durchführt, um sicherzustellen, dass sich diese Aktionen wie erwartet verhalten“, und dass es „automatisch wartet, bis alle relevanten Prüfungen bestanden sind, und erst dann die angeforderte Aktion ausführt“. Wenn die Prüfungen nicht rechtzeitig erfüllt werden, „schlägt die Aktion mit dem Fehler ‚TimeoutError‘ fehl.“
Die erforderlichen Prüfungen unterscheiden sich je nach Aktion, und dieser Unterschied ist diagnostisch relevant:
| Aktion | Erforderliche Prüfungen |
|---|---|
click, dblclick, check, uncheck, tap, setChecked | sichtbar, stabil, empfängt Ereignisse, aktiviert |
hover, dragTo | sichtbar, stabil, empfängt Ereignisse |
fill, clear | sichtbar, aktiviert |
selectOption | sichtbar, aktiviert |
screenshot, selectText | sichtbar |
scrollIntoViewIfNeeded | stabil |
blur, focus, press, pressSequentially, dispatchEvent, setInputFiles | keine |
Zwei Dinge fallen bei dieser Tabelle sofort ins Auge.
Ein click, bei dem eine Zeitüberschreitung auftritt, während fill auf demselben Element funktioniert, deutet auf „stable“ oder „receives events“ hin – das Element bewegt sich oder etwas befindet sich darüber. Animationen und Overlays sind die üblichen Verdächtigen.
Aktionen ohne Prüfungen sind ein Notausgang und ein Warnsignal. Wenn „locator.click()“ eine Zeitüberschreitung verursacht, „dispatchEvent('click')“ jedoch funktioniert, haben Sie nichts behoben – Sie haben lediglich die Prüfung umgangen, die Ihnen angezeigt hat, dass auch ein echter Nutzer dieses Element nicht anklicken konnte. Manchmal ist das akzeptabel. Meistens bedeutet es jedoch, dass ein echtes Overlay-Problem vorliegt, das Ihr Test einfach nicht mehr erkennt.
Timeouts an der richtigen Stelle ändern
Die Konfiguration ist auf mehreren Ebenen angelegt, und wenn man sie auf der falschen Ebene vornimmt, führt dies zu verwirrenden Ergebnissen.
Globale Konfiguration in „playwright.config.ts
“:
export default defineConfig({
timeout: 60_000,
globalTimeout: 60 * 60 * 1000,
expect: { timeout: 10_000 },
use: {
actionTimeout: 15_000,
navigationTimeout: 30_000,
},
});
Beachten Sie, wo sich die einzelnen Elemente befinden. timeout
und globalTimeout
gehören zur obersten Konfigurationsebene; expect.timeout
befindet sich unter expect
; actionTimeout
und navigationTimeout
befinden sich unter use
, da es sich um TestOptionen und nicht um Runner-Konfigurationen handelt. Werden sie auf der falschen Ebene platziert, werden sie stillschweigend ignoriert.
Pro Test:
test('slow one', async ({ page }) => {
test.setTimeout(120_000);
// ...
});
**test.slow()
** verdreifacht das Standard-Timeout – ein guter Standardwert für einen Test, von dem Sie wissen, dass er wirklich lange dauert, ohne dass Sie einen willkürlichen Wert wählen müssen.
Pro Assertion:
await expect(page.getByRole('status')).toHaveText('Done', { timeout: 30_000 });
Pro Aktion:
await page.getByRole('button', { name: 'Export' }).click({ timeout: 15_000 });
**In „beforeAll
“ und „afterAll
“**, die über ein eigenes 30-Sekunden-Kontingent verfügen, rufen Sie „test.setTimeout()
“ innerhalb des Hooks selbst auf.
Bei einem langsamen Fixture sollten Sie ihm in „test.extend()
“ ein eigenes Timeout zuweisen, anstatt das Timeout für jeden Test zu erhöhen, der dieses Fixture verwendet:
export const test = base.extend<{ seeded: void }>({
seeded: [async ({}, use) => {
await seedDatabase();
await use();
}, { timeout: 60_000 }],
});
Das allgemeine Prinzip: Legen Sie den engsten Geltungsbereich fest, der das Problem löst. Eine Erhöhung des globalen Test-Timeouts, um einem langsamen Test Rechnung zu tragen, führt dazu, dass alle anderen Tests langsamer fehlschlagen, was in der CI Echtzeit kostet.
Was auf das Test-Timeout angerechnet wird
Dies wird häufig missverstanden und erklärt, warum Tests „noch bevor sie etwas tun“ ein Timeout erreichen.
Die Dokumentation ist eindeutig: „Die Zeit, die von der Testfunktion, den Fixture-Einrichtungen und den Hooks von beforeEach verbraucht wird, wird auf das Test-Timeout angerechnet.“
Ein „beforeEach“, der sich anmeldet, Daten einliest und durch die Seiten navigiert, verbraucht also dieselben 30 Sekunden, die Ihr Testkörper benötigt. Ein Test, der scheinbar bereits in der ersten Zeile ein Timeout erreicht, hat möglicherweise 28 Sekunden für die Vorbereitung benötigt.
Fixtures teilen sich standardmäßig das Test-Timeout, was dieselbe Falle in anderer Form darstellt – ein ressourcenintensives Fixture verbraucht das Zeitbudget jedes Tests, der davon abhängt. Weisen Sie langsamen Fixtures ein eigenes Timeout zu, anstatt das Test-Timeout pauschal zu erhöhen.
Der Teardown ist getrennt: Fixture-Teardowns und „afterEach“-Hooks erhalten nach Abschluss der Testfunktion ihr eigenes Zeitbudget, sodass ein langsamer Teardown nicht die Zeit des Tests beansprucht.
Die praktische Konsequenz für die Fehlersuche: Wenn ein Test wegen Zeitüberschreitung abbricht, betrachten Sie die gesamte Kette – Fixtures, „beforeEach“ und den Testkörper – und nicht nur die Zeile, auf die der Fehler hinweist.
Erst diagnostizieren, dann erweitern
Eine Vorgehensweise, mit der sich die meisten Timeouts innerhalb weniger Minuten beheben lassen.
Führen Sie den Trace Viewer aus. Dies ist das wertvollste Tool überhaupt, das jedoch viel zu selten genutzt wird:
npx playwright test --trace on
npx playwright show-trace trace.zip
Der Trace zeigt jede Aktion, ihre Dauer, DOM-Snapshots davor und danach sowie die Netzwerkaktivität an. Ein Locator, der keine Übereinstimmung gefunden hat, ist sofort erkennbar; ebenso wie das Cookie-Banner, das über Ihrer Schaltfläche angezeigt wird.
Führen Sie die Ausführung „headed“ und verlangsamt“ durch, wenn Sie den Vorgang beobachten möchten:
npx playwright test --headed --debug
Überprüfen Sie, ob der Locator überhaupt aufgelöst wird:
console.log(await page.getByRole('button', { name: 'Save' }).count());
Der Wert Null deutet auf ein Selektorproblem hin, das sich durch keinen Timeout-Wert beheben lässt.
Prüfen Sie, ob es sich um ein Stabilitätsproblem handelt, indem Sie eine „check-free“-Aktion zur Diagnose verwenden – nicht als Lösung. Wenn dispatchEvent('click')
funktioniert, während click()
eine Zeitüberschreitung verursacht, wird das Element von etwas verdeckt oder verschoben.
**Prüfen Sie, ob eine „networkidle
“-Wartezeit vorliegt.** Seiten mit Analytics-Beacons, WebSockets oder Polling erreichen möglicherweise nie den Netzwerk-Leerlaufzustand. Warten Sie lieber auf das, was Ihnen tatsächlich wichtig ist:
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
Lesen Sie die Fehlermeldung vollständig durch. Die Timeout-Fehler von Playwright enthalten den Locator, die Anzahl der aufgelösten Elemente und die Angabe, welche Aktionierbarkeitsprüfung noch ausstehend war. Dieses letzte Detail benennt das Problem in der Regel direkt.
Das Erhöhen von Timeouts verschlimmert in der Regel die Unbeständigkeit
Der kontraintuitive, aber wichtige Aspekt.
Ein unzuverlässiger Test ist ein Test, dessen Ergebnis vom Zeitpunkt abhängt. Durch die Verlängerung des Timeouts vergrößert sich das Zeitfenster, in dem der Test erfolgreich ist, sodass die Unzuverlässigkeit seltener auftritt – und entsprechend schwerer zu reproduzieren, schwerer zu diagnostizieren und langsamer ist, wenn der Test doch fehlschlägt.
Gleichzeitig entstehen bei jedem Fehlschlag Kosten. Eine Testsuite mit 200 Tests und einem Timeout von 30 Sekunden benötigt höchstens 100 Minuten, bis sie vollständig fehlschlägt; bei 120 Sekunden sind es 400 Minuten. In der CI bedeutet das echtes Geld und echte Wartezeit.
Was Unbeständigkeit tatsächlich behebt:
Warte auf den Zustand, nicht auf die Zeit. „waitForTimeout“ ist fast immer falsch. Überprüfe die Bedingung, die dir wichtig ist, und lass Playwright abfragen.
Verwende „Web-First“-Assertions. „expect(locator).toBeVisible()“ führt automatisch Wiederholungsversuche durch. „expect(await locator.isVisible()).toBe(true)“ prüft einmal und schlägt beim ersten Fehlschlag fehl – eine subtile, aber sehr häufige Ursache für Unbeständigkeit.
Gehen Sie deterministisch mit Overlays um. Schließen Sie Cookie-Banner in einem Fixture, anstatt darauf zu hoffen, dass sie verschwunden sind.
Deaktivieren Sie Animationen in Ihrer Konfiguration, wo immer dies möglich ist, anstatt darauf zu warten, dass sie sich beruhigen.
Warten Sie auf die spezifische Netzwerkantwort, auf die Sie angewiesen sind, anstatt darauf zu warten, dass das Netzwerk stillsteht.
Stabilisieren Sie die Daten. Tests, die von gemeinsam genutzten, veränderbaren Zuständen abhängen, sind aus Gründen unzuverlässig, die sich nicht durch ein Timeout beheben lassen.
Wann sollte ein Timeout tatsächlich ausgelöst werden: Der Vorgang ist wirklich langsam und es gibt keinen Weg daran vorbei – ein Upload einer großen Datei, ein Bericht, dessen Erstellung eine Minute dauert, ein absichtlich gedrosseltes Netzwerkprofil. In diesen Fällen sollten Sie das Timeout gezielt auslösen, und zwar nur für diesen Test oder diese Assertion, und die Standardeinstellungen unverändert lassen.
Timeouts bei der Ausführung über einen Proxy
Der Fall, in dem ein „raise“ berechtigt ist, und die dazugehörige Konfiguration.
Playwright übernimmt Proxy-Einstellungen aus der Netzwerkkonfiguration:
export default defineConfig({
use: {
proxy: {
server: 'http://proxy.example.com:9000',
username: 'user',
password: 'pass',
},
},
});
Oder pro Kontext – was dann sinnvoll ist, wenn verschiedene Tests unterschiedliche Ausgangsorte benötigen:
const context = await browser.newContext({
proxy: { server: 'http://proxy.example.com:9000' },
});
Drei praktische Konsequenzen.
Privathaushalts-Proxys verursachen echte Latenz. Der Datenverkehr wird über eine echte Privatkundenverbindung geleitet, daher sind mehrere hundert zusätzliche Millisekunden pro Anfrage normal und kein Fehler. Eine Seite mit achtzig Anfragen summiert diese Verzögerung achtzigmal. Standardwerte, die auf „localhost“ abgestimmt sind, führen zu Fehlern, die wie defekte Proxys aussehen, tatsächlich aber nur auf die Entfernung zurückzuführen sind.
Die richtige Vorgehensweise ist, zu messen statt zu raten: Führen Sie die Testsuite über den Proxy aus, überprüfen Sie die Trace-Zeiten und legen Sie „navigationTimeout
“ und „actionTimeout
“ anhand Ihrer Beobachtungen mit einem großzügigen Spielraum fest.
Die Bandbreite ist der eigentliche Kostenfaktor, und bei einem Browser ist dieser enorm. Playwright lädt jedes Bild, jede Schriftart, jedes Skript und jedes Video vorab. Bei begrenztem Privatkunden-Datenverkehr zu 0,79 $/GB dominiert dies alle anderen Ausgaben. Das Blockieren unnötiger Ressourcentypen ist die größte Einzelersparnis, die sich erzielen lässt:
await page.route('**/*.{png,jpg,jpeg,webp,gif,woff,woff2,mp4}', r => r.abort());
Dadurch wird der Datenverkehr in der Regel um den größten Teil der Gesamtmenge reduziert, was als Nebeneffekt Ihre Tests beschleunigt.
Eine Blockierung ist kein Timeout. Wenn ein Ziel eine Challenge-Seite ausliefert, kommt es bei Playwright zu einem Timeout beim Warten auf ein Element, das nicht auf der Challenge-Seite vorhanden ist – was genau wie ein Timeout aussieht, aber keines ist. Machen Sie bei einem Fehler einen Screenshot und schauen Sie sich an, was tatsächlich gerendert wurde:
use: { screenshot: 'only-on-failure', trace: 'retain-on-failure' }
Dies ist das Muster des „stillen Fehlers“, das wir in Warum das Testen von Proxys wichtig ist beschrieben haben: Die Anfrage war erfolgreich, die Seite wurde gerendert, und es war die falsche Seite.
Häufig gestellte Fragen
Wie lang ist die Standard-Zeitüberschreitung in Playwright?
30.000 ms für einen Test und für die Hooks „beforeAll“ / „afterAll“ sowie 5.000 ms für „expect“-Assertions. Zeitlimits für Aktionen und Navigationen haben keinen Standardwert und werden ausschließlich durch das Test-Timeout begrenzt. Aus diesem Grund meldet ein langsamer Klick die 30 Sekunden des Tests und nicht sein eigenes Limit.
Wie erhöhe ich das Timeout für einen einzelnen Playwright-Test?
Rufen Sie innerhalb des Tests test.setTimeout(120_000) oder test.slow() auf, um den Standardwert zu verdreifachen. Bevorzugen Sie diese Vorgehensweise gegenüber einer Erhöhung des globalen Timeouts, da dies dazu führt, dass alle anderen Tests langsamer fehlschlagen.
Warum läuft mein Playwright-Test ab, obwohl das Element auf der Seite vorhanden ist?
In der Regel, weil es nicht interagierbar ist. „click“ setzt voraus, dass das Element sichtbar, stabil, ereignisempfänglich und aktiviert ist – ein Element, das also von einem Banner verdeckt wird oder sich noch in einer Animation befindet, wird zwar gefunden, aber niemals angeklickt. Die Fehlermeldung gibt an, welche Prüfung noch aussteht.
Was ist der Unterschied zwischen Test-Timeout und „expect“-Timeout?
Das Test-Timeout ist die Gesamtzeit, die für die Testfunktion, die Einrichtung der Fixtures und die „beforeEach“-Hooks zusammen zur Verfügung steht; der Standardwert beträgt 30 Sekunden. Das „Expect“-Timeout gibt an, wie lange eine einzelne „Web-First“-Assertion abgefragt wird; der Standardwert beträgt 5 Sekunden. Wenn eine Assertion nach 5 Sekunden fehlschlägt, handelt es sich um das „Expect“-Timeout, nicht um das Test-Timeout.
Sollte ich „waitForTimeout“ in Playwright verwenden?
So gut wie nie. Eine feste Wartezeit ist entweder zu kurz, was den Test unzuverlässig macht, oder zu lang, was die Testsuite verlangsamt – meist beides auf verschiedenen Rechnern. Warte stattdessen mit einer „Web-First“-Assertion auf die Bedingung, die automatisch so lange wiederholt wird, bis das Timeout erreicht ist.
Warum wird „networkidle“ nie aufgelöst?
Weil die Seite ständig Anfragen stellt – Analytics-Beacons, WebSockets, Abfragen, langlebige Verbindungen. „networkidle“ erfordert Ruhe, und viele moderne Anwendungen werden nie ruhig. Warten Sie stattdessen auf das spezifische Element oder die Antwort, die für Sie von Bedeutung ist.
Behebt das Erhöhen des Timeouts unzuverlässige Tests?
Es verbirgt sie. Die Unbeständigkeit tritt seltener auf, ist schwerer zu reproduzieren und führt langsamer zum Fehlschlag, während jeder echte Fehlschlag in der Testsuite nun länger dauert. Beheben Sie die Ursache – warten Sie auf den Zustand statt auf die Zeit, verwenden Sie Assertions mit Wiederholungsversuchen, schließen Sie Overlays deterministisch und deaktivieren Sie Animationen.
Benötige ich längere Timeouts, wenn ich einen Proxy verwende?
Oftmals ja, insbesondere für Navigation und Aktionen, da Residential-Proxys eine reale Latenz pro Anfrage verursachen, die sich über die vielen Anfragen einer Seite hinweg summiert. Messen Sie diese Latenz über den Proxy bei aktivierter Ablaufverfolgung und legen Sie die Werte anhand Ihrer Beobachtungen fest, anstatt alles vorsorglich zu erhöhen.
Fazit
Die Meldung besagt, dass der Test 30 Sekunden überschritten hat – das ist jedoch eher eine Vorgabe als eine Erklärung. Etwas im Test hat auf eine Bedingung gewartet, die nie eingetreten ist, und in vier von fünf Fällen handelt es sich bei dieser Bedingung um einen Selektor, der zu keinem Ergebnis führt, oder um ein Element, das nie auslösbar wurde.
Die Reihenfolge, mit der man Zeit spart, lautet also: Lies den vollständigen Fehlertext, der die ausstehende Überprüfbarkeit nennt; öffne den Trace, der dir das DOM zum Zeitpunkt des Fehlers anzeigt; vergewissere dich, dass der Locator aufgelöst wird; und erst dann betrachte die Zahl. Das Auslösen eines Timeouts ist genau für eine Ursache die richtige Lösung – nämlich für einen Vorgang, der tatsächlich länger dauert als das Zeitlimit – und die falsche Lösung für die anderen vier Fälle, in denen es lediglich zu einem langsameren Fehlerausfall führt.
Wenn Sie ein Timeout auslösen, tun Sie dies gezielt. Pro Test, pro Assertion, pro Fixture. Das Aufblähen der globalen Standardwerte, um einen einzigen langsamen Upload zu berücksichtigen, verteuert jeden Fehler in der Testsuite, und CI-Zeit ist die einzige Ressource, die nie wieder zurückkommt.
