Ich habe es immer genossen, meinen Unit-Tests beim Laufen (und Bestehen) zuzusehen. Sie sind schnell und das Bestehen von Tests gibt mir die Gewissheit, dass sich meine einzelnen Teile so verhalten, wie sie sollen. Umgekehrt hatte ich oft Schwierigkeiten, End-to-End-Tests für den Browser zu priorisieren, weil das Schreiben und Ausführen dieser Tests extrem langsam war.
Glücklicherweise sind die Tools für End-to-End-In-Browser-Tests im Laufe der Jahre viel besser und schneller geworden. Und mit einem Headless-Browser-Setup kann ich meine Browsertests als Teil meines CI ausführen.
Kürzlich bin ich auf diesen Heroku-Blogbeitrag gestoßen, in dem es um die Automatisierung von In-Browser-Tests mit Headless Chrome innerhalb von Heroku CI geht. Heroku verfügt über ein Buildpack, das Headless Chrome installiert, das Sie für Ihre Tests in der CI-Pipeline aufrufen können.
Das Beispiel-Setup aus dem Blog-Beitrag war eine React-App, die mit Puppeteer und Jest getestet wurde. Das ist ein toller Anfang … aber was ist, wenn ich Playwright anstelle von Puppeteer verwende? Ist es möglich?
Ich beschloss, Nachforschungen anzustellen. Wie sich herausstellt – ja, das können Sie auch mit Playwright machen! Deshalb habe ich die Schritte erfasst, die Sie benötigen, um Playwright-Tests auf dem in Heroku CI verwendeten Headless-Chrome-Browser auszuführen. In diesem Beitrag führe ich Sie durch die Schritte zur Einrichtung.
End-to-End-Tests erfassen, wie Benutzer tatsächlich mit Ihrer App in einem Browser interagieren, und validieren vollständige Arbeitsabläufe. Playwright macht diesen Prozess durch Tests in Chrome, Firefox und Safari ziemlich reibungslos. Natürlich ist die Durchführung einer ganzen Reihe von Browsertests in CI ziemlich aufwändig, weshalb der Headless-Modus hilfreich ist.
Das Chrome for Testing-Buildpack von Heroku installiert Chrome auf einer Heroku-App, sodass Sie Ihre Playwright-Tests in Heroku CI mit einem wirklich schlanken Setup ausführen können.
Da ich das gerade ausprobiert habe, habe ich das GitHub-Repo geforkt, auf das ursprünglich im Heroku-Blogbeitrag verwiesen wurde. Bei der Anwendung handelte es sich um eine einfache React-App mit einem Link, einer Texteingabe und einer Schaltfläche zum Senden. Es gab drei Tests:
Überprüfen Sie, ob der Link funktioniert und an den richtigen Ort weiterleitet.
Überprüfen Sie, ob die Texteingabe die Benutzereingabe richtig anzeigt.
Stellen Sie sicher, dass durch das Absenden des Formulars der auf der Seite angezeigte Text aktualisiert wird.
Ganz einfach. Jetzt musste ich nur noch den Code ändern, um Playwright anstelle von Puppeteer und Jest zu verwenden. Oh, und ich wollte auch pnpm anstelle von npm verwenden. Hier ist ein Link zu meinem geforkten GitHub-Repo.
Lass uns die Schritte durchgehen, die ich unternommen habe, um den Code zu ändern. Ich habe mit meinem Forked-Repo begonnen, das mit dem Heroku-Examples-Repo identisch ist.
Ich wollte pnpm anstelle von npm verwenden. (Persönliche Präferenz.) Also, hier ist, was ich zuerst gemacht habe:
~/project$ corepack enable pnpm ~/project$ corepack use pnpm@latest Installing pnpm@9.12.3 in the project… … Progress: resolved 1444, reused 1441, downloaded 2, added 1444, done … Done in 14.4s ~/project$ rm package-lock.json ~/project$ pnpm install # just to show everything's good Lockfile is up to date, resolution step is skipped Already up to date Done in 1.3s
Als nächstes habe ich Puppeteer und Jest entfernt und Playwright hinzugefügt.
~/project$ pnpm remove \ babel-jest jest jest-puppeteer @testing-library/jest-dom ~/project$ $ pnpm create playwright Getting started with writing end-to-end tests with Playwright: Initializing project in '.' ✔ Do you want to use TypeScript or JavaScript? · JavaScript ✔ Where to put your end-to-end tests? · tests ✔ Add a GitHub Actions workflow? (y/N) · false ✔ Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? (Y/n) · false ✔ Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo pnpm exec playwright install-deps')? (y/N) · false Installing Playwright Test (pnpm add --save-dev @playwright/test)… … Installing Types (pnpm add --save-dev @types/node)… … Done in 2.7s Writing playwright.config.js. Writing tests/example.spec.js. Writing tests-examples/demo-todo-app.spec.js. Writing package.json.
Ich habe auch den Jest-Konfigurationsabschnitt aus package.json entfernt.
Sie können Ihre Playwright-Tests in Chrome, Firefox und Safari ausführen. Da ich mich auf Chrome konzentrierte, habe ich die anderen Browser aus dem Projektabschnitt der generierten Datei playwright.config.js entfernt:
/* Configure projects for major browsers */ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, // { // name: 'firefox', // use: { ...devices['Desktop Firefox'] }, // }, // // { // name: 'webkit', // use: { ...devices['Desktop Safari'] }, // }, ], …
Der Originalcode hatte eine Puppeteer-Testdatei unter src/tests/puppeteer.test.js. Ich habe diese Datei nach tests/playwright.spec.js verschoben. Dann habe ich den Test aktualisiert, um die Konventionen von Playwright zu verwenden, die recht sauber abgebildet wurden. Die neue Testdatei sah so aus:
const ROOT_URL = 'http://localhost:8080'; const { test, expect } = require('@playwright/test'); const inputSelector = 'input[name="name"]'; const submitButtonSelector = 'button[type="submit"]'; const greetingSelector = 'h5#greeting'; const name = 'John Doe'; test.beforeEach(async ({ page }) => { await page.goto(ROOT_URL); }); test.describe('Playwright link', () => { test('should navigate to Playwright documentation page', async ({ page }) => { await page.click('a[href="https://playwright.dev/"]'); await expect(page.title()).resolves.toMatch('| Playwright'); }); }); test.describe('Text input', () => { test('should display the entered text in the text input', async ({ page }) => { await page.fill(inputSelector, name); // Verify the input value const inputValue = await page.inputValue(inputSelector); expect(inputValue).toBe(name); }); }); test.describe('Form submission', () => { test('should display the "Hello, X" message after form submission', async ({ page }) => { const expectedGreeting = `Hello, ${name}.`; await page.fill(inputSelector, name); await page.click(submitButtonSelector); await page.waitForSelector(greetingSelector); const greetingText = await page.textContent(greetingSelector); expect(greetingText).toBe(expectedGreeting); }); });
Um meine React-App zu testen, musste ich sie zunächst in einem separaten Prozess starten (unter http://localhost:8080) und dann konnte ich meine Tests ausführen. Dies wäre der Fall, unabhängig davon, ob ich Puppeteer oder Playwright verwende. Bei Puppeteer verwendete das Heroku-Beispiel das Paket start-server-and-test. Sie können Playwright jedoch so konfigurieren, dass die App vor dem Ausführen von Tests gestartet wird. Das ist ziemlich praktisch!
Ich habe „start-server-and-test“ aus meinem Projekt entfernt.
~/project$ pnpm remove start-server-and-test
In playwright.config.js habe ich den Abschnitt „webServer“ unten auskommentiert und ihn so geändert, dass er wie folgt aussieht:
/* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', url: 'http://127.0.0.1:8080', reuseExistingServer: !process.env.CI, },
Dann habe ich das test:ci-Skript aus der ursprünglichen package.json-Datei entfernt. Stattdessen sah mein Testskript so aus:
"scripts": { … "test": "playwright test --project=chromium --reporter list" },
Playwright installiert die neuesten Browser-Binärdateien für seine Tests. Also brauchte ich Playwright, um auf meinem lokalen Rechner seine Version von Chromium zu installieren.
~/project$ pnpm playwright install chromium Downloading Chromium 130.0.6723.31 (playwright build v1140) from https://playwright.azureedge.net/builds/chromium/1140/chromium-linux.zip 164.5 MiB [====================] 100%
Hinweis: Das Chrome for Testing-Buildpack auf Heroku installiert den Browser, den wir zum Testen verwenden. Wir richten unser CI so ein, dass Playwright diesen Browser verwendet, anstatt Zeit und Ressourcen für die Installation eines eigenen Browsers aufzuwenden.
Damit war ich startklar. Es war Zeit, meine Tests vor Ort auszuprobieren.
~/project$ pnpm test > playwright test --project=chromium --reporter list Running 3 tests using 3 workers ✓ 1 [chromium] > playwright.spec.js:21:3 > Text input > should display the entered text in the text input (911ms) ✘ 2 [chromium] > playwright.spec.js:14:3 > Playwright link > should navigate to Playwright documentation page (5.2s) ✓ 3 [chromium] > playwright.spec.js:31:3 > Form submission > should display the "Hello, X" message after form submission (959ms) ... - waiting for locator('a[href="https://playwright.dev/"]') 13 | test.describe('Playwright link', () => { 14 | test('should navigate to Playwright documentation page', async ({ page }) => { > 15 | await page.click('a[href="https://playwright.dev/"]'); | ^ 16 | await expect(page.title()).resolves.toMatch('| Playwright'); 17 | }); 18 | });
Oh! Das ist richtig. Ich habe meinen Test dahingehend geändert, dass ich über den Link in der App zur Dokumentation von Playwright und nicht zur Dokumentation von Puppeteer weitergeleitet werde. Ich musste src/App.js in Zeile 19 aktualisieren:
<Link href="https://playwright.dev/" rel="noopener"> Playwright Documentation </Link>
Jetzt war es an der Zeit, die Tests erneut durchzuführen…
~/project$ pnpm test > playwright test --project=chromium --reporter list Running 3 tests using 3 workers ✓ 1 [chromium] > playwright.spec.js:21:3 > Text input > should display the entered text in the text input (1.1s) ✓ 2 [chromium] > playwright.spec.js:14:3 > Playwright link > should navigate to Playwright documentation page (1.1s) ✓ 3 [chromium] > playwright.spec.js:31:3 > Form submission > should display the "Hello, X" message after form submission (1.1s) 3 passed (5.7s)
Die Tests bestanden! Als nächstes war es an der Zeit, uns auf Heroku CI zu bringen.
Ich habe die Anweisungen im Heroku-Blogbeitrag befolgt, um meine App in einer Heroku-CI-Pipeline einzurichten.
In Heroku habe ich eine neue Pipeline erstellt und sie mit meinem gespaltenen GitHub-Repo verbunden.
Als nächstes habe ich meine App zum Staging hinzugefügt.
Dann ging ich zur Registerkarte Tests und klickte auf Heroku CI aktivieren.
Schließlich habe ich die Datei app.json geändert, um das Testskript zu entfernen, das auf den Aufruf von npm test:ci eingestellt war. Ich hatte das test:ci-Skript bereits aus meiner package.json-Datei entfernt. Nun wurde das Testskript in package.json verwendet, und Heroku CI würde standardmäßig danach suchen.
Meine app.json-Datei, die sicherstellte, dass das Chrome for Testing-Buildpack verwendet wurde, sah so aus:
~/project$ corepack enable pnpm ~/project$ corepack use pnpm@latest Installing pnpm@9.12.3 in the project… … Progress: resolved 1444, reused 1441, downloaded 2, added 1444, done … Done in 14.4s ~/project$ rm package-lock.json ~/project$ pnpm install # just to show everything's good Lockfile is up to date, resolution step is skipped Already up to date Done in 1.3s
Ich habe meinen Code auf GitHub übertragen und dies hat einen Testlauf in Heroku CI ausgelöst.
Der Testlauf schlug fehl, aber ich machte mir keine Sorgen. Ich wusste, dass einige Playwright-Konfigurationen vorgenommen werden mussten.
Beim Durchstöbern des Testprotokolls habe ich Folgendes gefunden:
~/project$ pnpm remove \ babel-jest jest jest-puppeteer @testing-library/jest-dom ~/project$ $ pnpm create playwright Getting started with writing end-to-end tests with Playwright: Initializing project in '.' ✔ Do you want to use TypeScript or JavaScript? · JavaScript ✔ Where to put your end-to-end tests? · tests ✔ Add a GitHub Actions workflow? (y/N) · false ✔ Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? (Y/n) · false ✔ Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo pnpm exec playwright install-deps')? (y/N) · false Installing Playwright Test (pnpm add --save-dev @playwright/test)… … Installing Types (pnpm add --save-dev @types/node)… … Done in 2.7s Writing playwright.config.js. Writing tests/example.spec.js. Writing tests-examples/demo-todo-app.spec.js. Writing package.json.
Playwright suchte nach der Chrome-Browserinstanz. Ich konnte es mit dem Befehl „playwright install chromium“ als Teil meines CI-Test-Setups installieren. Aber das würde den ganzen Zweck des Chrome for Testing-Buildpacks zunichte machen. Chrome war bereits installiert; Ich musste nur richtig darauf hinweisen.
Als ich in meinem Test-Setup-Protokoll für Heroku nachschaute, fand ich diese Zeilen:
/* Configure projects for major browsers */ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, // { // name: 'firefox', // use: { ...devices['Desktop Firefox'] }, // }, // // { // name: 'webkit', // use: { ...devices['Desktop Safari'] }, // }, ], …
Der Browser, den ich verwenden wollte, war also /app/.chrome-for-testing/chrome-linux64/chrome. Ich bräuchte nur Playwright, der dort danach sucht.
Hinweis: Wenn Sie hier nicht an den Details interessiert sind, können Sie diesen Abschnitt überspringen und einfach die vollständige app.json weiter unten kopieren. Dies sollte Ihnen alles geben, was Sie brauchen, um mit Playwright auf Heroku CI loszulegen.
In der Dokumentation von Playwright habe ich herausgefunden, dass Sie eine Umgebungsvariable festlegen können, die Playwright mitteilt, ob Sie für alle Browserinstallationen einen benutzerdefinierten Speicherort verwendet haben. Diese Umgebungsvariable ist PLAYWRIGHT_BROWSERS_PATH. Ich beschloss, dort anzufangen.
In app.json habe ich eine Umgebungsvariable wie diese festgelegt:
const ROOT_URL = 'http://localhost:8080'; const { test, expect } = require('@playwright/test'); const inputSelector = 'input[name="name"]'; const submitButtonSelector = 'button[type="submit"]'; const greetingSelector = 'h5#greeting'; const name = 'John Doe'; test.beforeEach(async ({ page }) => { await page.goto(ROOT_URL); }); test.describe('Playwright link', () => { test('should navigate to Playwright documentation page', async ({ page }) => { await page.click('a[href="https://playwright.dev/"]'); await expect(page.title()).resolves.toMatch('| Playwright'); }); }); test.describe('Text input', () => { test('should display the entered text in the text input', async ({ page }) => { await page.fill(inputSelector, name); // Verify the input value const inputValue = await page.inputValue(inputSelector); expect(inputValue).toBe(name); }); }); test.describe('Form submission', () => { test('should display the "Hello, X" message after form submission', async ({ page }) => { const expectedGreeting = `Hello, ${name}.`; await page.fill(inputSelector, name); await page.click(submitButtonSelector); await page.waitForSelector(greetingSelector); const greetingText = await page.textContent(greetingSelector); expect(greetingText).toBe(expectedGreeting); }); });
Ich habe meinen Code auf GitHub übertragen, um zu sehen, was mit meinen Tests in CI passieren würde.
Wie erwartet ist es erneut fehlgeschlagen. Der Protokollfehler zeigte jedoch Folgendes:
~/project$ corepack enable pnpm ~/project$ corepack use pnpm@latest Installing pnpm@9.12.3 in the project… … Progress: resolved 1444, reused 1441, downloaded 2, added 1444, done … Done in 14.4s ~/project$ rm package-lock.json ~/project$ pnpm install # just to show everything's good Lockfile is up to date, resolution step is skipped Already up to date Done in 1.3s
Das hat mich ziemlich nahe gebracht. Ich habe beschlossen, Folgendes zu tun:
~/project$ pnpm remove \ babel-jest jest jest-puppeteer @testing-library/jest-dom ~/project$ $ pnpm create playwright Getting started with writing end-to-end tests with Playwright: Initializing project in '.' ✔ Do you want to use TypeScript or JavaScript? · JavaScript ✔ Where to put your end-to-end tests? · tests ✔ Add a GitHub Actions workflow? (y/N) · false ✔ Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? (Y/n) · false ✔ Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo pnpm exec playwright install-deps')? (y/N) · false Installing Playwright Test (pnpm add --save-dev @playwright/test)… … Installing Types (pnpm add --save-dev @types/node)… … Done in 2.7s Writing playwright.config.js. Writing tests/example.spec.js. Writing tests-examples/demo-todo-app.spec.js. Writing package.json.
/* Configure projects for major browsers */ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, // { // name: 'firefox', // use: { ...devices['Desktop Firefox'] }, // }, // // { // name: 'webkit', // use: { ...devices['Desktop Safari'] }, // }, ], …
Allerdings hatte ich Bedenken, ob dies zukunftssicher sein würde. Irgendwann würde Playwright eine neue Version von Chromium verwenden und diese würde nicht mehr in einem Chromium-1140-Ordner suchen. Wie könnte ich herausfinden, wo Playwright suchen würde?
Da entdeckte ich, dass Sie einen Probelauf für die Browserinstallation durchführen können.
const ROOT_URL = 'http://localhost:8080'; const { test, expect } = require('@playwright/test'); const inputSelector = 'input[name="name"]'; const submitButtonSelector = 'button[type="submit"]'; const greetingSelector = 'h5#greeting'; const name = 'John Doe'; test.beforeEach(async ({ page }) => { await page.goto(ROOT_URL); }); test.describe('Playwright link', () => { test('should navigate to Playwright documentation page', async ({ page }) => { await page.click('a[href="https://playwright.dev/"]'); await expect(page.title()).resolves.toMatch('| Playwright'); }); }); test.describe('Text input', () => { test('should display the entered text in the text input', async ({ page }) => { await page.fill(inputSelector, name); // Verify the input value const inputValue = await page.inputValue(inputSelector); expect(inputValue).toBe(name); }); }); test.describe('Form submission', () => { test('should display the "Hello, X" message after form submission', async ({ page }) => { const expectedGreeting = `Hello, ${name}.`; await page.fill(inputSelector, name); await page.click(submitButtonSelector); await page.waitForSelector(greetingSelector); const greetingText = await page.textContent(greetingSelector); expect(greetingText).toBe(expectedGreeting); }); });
Diese Zeile „Installationsort“ war entscheidend. Und wenn wir PLAYWRIGHT_BROWSERS_PATH festlegen, sehen wir Folgendes:
~/project$ pnpm remove start-server-and-test
Das ist es, was ich will. Mit ein wenig Awk-Magie habe ich Folgendes geschafft:
/* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', url: 'http://127.0.0.1:8080', reuseExistingServer: !process.env.CI, },
Nachdem ich das alles herausgefunden hatte, musste ich einfach ein Test-Setup-Skript zu app.json hinzufügen. Da PLAYWRIGHT_BROWSERS_PATH bereits in env festgelegt ist, wäre mein Skript etwas einfacher. Dies war meine letzte app.json-Datei:
"scripts": { … "test": "playwright test --project=chromium --reporter list" },
Ich werde kurz erläutern, was das Test-Setup bewirkt:
Unter Berücksichtigung von PLAYWRIGHT_BROWSERS_PATH wird die Playwright-Installation verwendet – führen Sie einen Probelauf mit awk durch, um den Stammordner zu ermitteln, in dem Playwright nach dem Chrome-Browser sucht. Legt dies als Wert für die Variable CHROMIUM_PATH fest.
Erstellt einen neuen Ordner (und alle erforderlichen übergeordneten Ordner) in CHROMIUM_PATH/chrome-linux, dem tatsächlichen Ordner, in dem Playwright nach der Chrome-Binärdatei sucht.
Erstellt einen symbolischen Link in diesem Ordner, damit Chrome auf die Heroku-Buildpack-Installation von Chrome verweist (/app/.chrome-for-testing/chrome-linux64/chrome).
Mit meiner aktualisierten app.json-Datei sollte Playwright in der Lage sein, die Chrome-Installation aus dem Buildpack zu verwenden. Es war Zeit, die Tests noch einmal durchzuführen.
Erfolg!
Das Test-Setup-Skript lief wie erwartet.
Playwright konnte auf die Chrome-Binärdatei zugreifen und die Tests ausführen, die bestanden wurden.
End-to-End-Tests für meine Webanwendungen werden immer weniger umständlich, daher gebe ich ihnen immer mehr Priorität. In den letzten Tagen bedeutete das auch, Playwright stärker zu nutzen. Es ist flexibel und schnell. Und jetzt, da ich die Arbeit (für mich und für Sie!) erledigt habe, um es mit dem Chrome for Testing-Buildpack in Heroku CI zum Laufen zu bringen, kann ich einmal mit dem Aufbau meiner Browser-Automatisierungstestsuiten beginnen schon wieder.
Der Code für diese exemplarische Vorgehensweise ist in meinem GitHub-Repository verfügbar.
Viel Spaß beim Codieren!
Das obige ist der detaillierte Inhalt vonDramatiker- und Chrome-Browsertests in Heroku. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!