Als wir die Anfrage erhielten, die Cypress-Tests nach Playwright zu migrieren, erwarteten wir einen langen, mühsamen Umschreibungsprozess.
Überraschenderweise gelang es uns mit Hilfe von GitHub Copilot, die Migration von Cypress zu Playwright in nur wenigen Tagen zu starten — und unseren CI-Pipeline die ganze Zeit über ohne Unterbrechung der Testprozesse am Laufen zu halten.
In diesem Artikel werde ich teilen, wie wir diese Migration angegangen sind, welche Lektionen wir auf dem Weg gelernt haben und wie KI-unterstützte Code-Migration den manuellen Aufwand bei Rahmenübergängen drastisch reduzieren kann.
Projektkontext
Das Projekt war eine komplexe Gesundheitsplattform mit viel im Hintergrund. Unsere Testkonfiguration umfasste:
- End-to-End- (E2E) und Integrationstests
- Eine benutzerdefinierte TAF-Architektur mit Setups, Teardowns und Entitätskreation
- Multi-Umgebungs-Konfigurationen
- Dynamische Daten und tiefe Navigationslogik
Insgesamt hatten wir 148 E2E-Tests, die auf Cypress liefen — alle funktionierten einwandfrei, aber die Organisation wollte die QA-Tools über die Teams hinweg standardisieren, was nur eines bedeutete: Migration zu Playwright.
Warum GitHub Copilot für die Migration verwenden?
Als die Migrationsanfrage eintraf, sah ich die Gelegenheit, mit GitHub Copilot zur Testautomatisierung zu experimentieren. Anstatt Hunderte von Testdateien manuell umzuschreiben, wollte ich sehen, wie weit wir die KI-Unterstützung nutzen konnten, um die Syntaxkonvertierung von Cypress nach Playwright zu automatisieren.
Und ehrlich gesagt, es hat viel besser funktioniert als erwartet.
GitHub Copilot übernahm die meisten wiederholenden Übersetzungen (Auswähler, Befehle, asynchrone Verarbeitung usw.), sodass wir uns auf Infrastruktur-Anpassungen und Feinabstimmungen konzentrieren konnten.
Innerhalb von 1–2 Tagen hatten wir bereits einen funktionierenden Prototyp des Playwright-Testautomatisierungsrahmens.
Zwei wichtige Hinweise
Überprüfen Sie immer den von der KI generierten Code
Auch wenn die KI-unterstützte Code-Migration die Dinge beschleunigt, müssen Sie jede Zeile überprüfen. Infrastruktur-Migrationen können leicht subtile Fehler oder Leistungsverschlechterungen einführen. Denken Sie an Copilot als Ihren Programmierpartner, nicht als Autopilot.
Behalten Sie das alte Framework bis zum Ende in Betrieb
Blockieren Sie niemals Releases oder Regressionen während der Migration. Lassen Sie Ihre Cypress-Tests im CI weiterlaufen, bis die entsprechenden Suiten vollständig stabil in Playwright sind. Sobald eine Suite migriert und validiert ist, entfernen Sie sie aus Cypress und schalten Sie das CI um, um sie in Playwright auszuführen.
Diese schrittweise Einführung gewährleistet null Ausfallzeiten und kontinuierliche Testabdeckung während des gesamten Prozesses.
Workflow zur Migration von Cypress nach Playwright
Sobald die Grundlagen klar waren, gingen wir in die praktische Migration über.
Im Folgenden ist der Workflow aufgeführt, der uns half, beide Rahmen parallel auszuführen, die CI-Stabilität aufrechtzuerhalten und Tests schrittweise zu migrieren — ohne laufende Releases zu blockieren.
1. Playwright initialisieren und die Ordnerstruktur erstellen
Der erste Schritt bestand darin, Playwright zu initialisieren und eine saubere, isolierte Ordnerstruktur zu erstellen. Wir wollten, dass beide Frameworks nebeneinander im selben Repository leben, damit wir sie während der Übergangsphase gleichzeitig ausführen konnten.
Diese Einrichtung ermöglichte es uns:
- Allmähliche Migration
- Ergebnisse und Zeitmessungen zwischen Cypress und Playwright zu vergleichen
- CI-Pipelines für beide am Laufen zu halten
Beispiel zur Initialisierung:

Hier ist die vorgeschlagene Ordnerstruktur, die wir für Playwright verwendet haben:
├── cypress/
│ ├── app/
│ ├── downloads/
│ ├── e2e/
│ ├── fixtures/
│ ├── screenshots/
│ ├── support/
│ └── videos/
├── playwright/
│ ├── app/
│ │ ├── components/
│ │ ├── fixtures/
│ │ └── pageobjects/
│ ├── constants/
│ ├── helpers/
│ ├── reports/
│ ├── test-results/
│ └── tests/
├── .gitignore
├── package.json
├── playwright.config.ts
├── smoke.config.ts
├── tsconfig.json2. Konfigurieren Sie separate tsconfig-Dateien für jedes Framework
Ein frühes Problem, mit dem wir konfrontiert waren, waren TypScript-Konflikte — beide Frameworks definieren ähnliche Methodennamen (expect, request usw.), und die gemeinsame Nutzung einer einzigen tsconfig.json führte zu Kompilierungsfehlern.
Die Lösung war einfach: Teilen Sie die TypeScript-Konfigurationen. Diese Struktur sorgt dafür, dass jedes Framework seine eigenen Typdefinitionen initialisiert und Typenkollisionen vermeidet:
├── tsconfig.json
├── tsconfig.cypress.json
├── tsconfig.playwright.jsonHaupt tsconfig.json
Fügen Sie die Option composite hinzu, um Projektverweise zu unterstützen.
tsconfig.json
{
"compilerOptions": {
"composite": true
}
}tsconfig.cypress.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": ["cypress", "@testing-library/cypress", "node"],
"isolatedModules": false
},
"include": ["cypress/**/*.ts"]
}tsconfig.playwright.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": ["node", "@playwright/test"]
},
"include": ["playwright/**/*.ts", "playwright.config.ts"]
}Mit dieser Trennung konnten beide Frameworks friedlich in einem Repository coexistieren — keine Compilerkonflikte mehr und kein Risiko, versehentlich gemeinsam genutzte Methoden zweimal zu initialisieren.
3. Erstellen Sie eine Aufforderung zur Automatisierung der Migration mit GitHub Copilot
Sobald Playwright initialisiert und das Projekt strukturiert war, war es an der Zeit, GitHub Copilot bei der schweren Arbeit zu helfen.
Dazu haben wir eine benutzerdefinierte Aufforderung erstellt, die definiert, wie Copilot sich verhalten soll, wenn Tests von Cypress zu Playwright umgeschrieben werden. Die Aufforderung sagt dem Agenten, was zu tun ist, wie es zu tun ist und was ignoriert werden soll — und sorgt so für konsistente Ergebnisse bei allen Migrationen.
So fügen Sie die Aufforderung hinzu
Öffnen Sie in Ihrem GitHub-Repository das Zahnrad-Symbol ⚙️ im Copilot-Chat und erstellen Sie eine neue Aufforderung.
Die Datei wird automatisch unter gespeichert: ./github/prompts/
Sie können sie beispielsweise migrate_tests.md nennen.
Beispiel für die Aufforderung:
---
mode: agent
---
You are a Playwright Test Generator, an expert in browser automation and end-to-end testing.
Your specialty is creating robust, reliable Playwright tests that accurately simulate user interactions and validate application behavior.
Your task is to help migrate existing Cypress tests to Playwright tests.
The Cypress tests, page object, components, and helper files are provided as input, and you need to generate equivalent Playwright test code.
Output ALL migrated code to the chat.
When generating the Playwright test code or any page objects, ensure that:
1. The test structure follows the same structure as the Cypress tests.
2. Create a global fixture for setup and teardown and reuse it in the tests.
3. All user interactions (clicks, typing, navigation) are accurately translated to Playwright syntax in page objects and test files.
4. Assertions in Cypress are converted to equivalent Playwright assertions.
5. Ignore the network spying and stubbing parts of the Cypress tests.
6. Comment out all the methods with waits (e.g., cy.wait) in the Playwright code.
7. Do not use get methods in page objects; use direct methods instead `backbackItem = () => this.page.locator('[id="item_4_title_link"]');`Warum das hilft
Diese Art von strukturierter Aufforderung ermöglicht es GitHub Copilot, sich eher wie ein spezialisierter Migrationsassistent zu verhalten und nicht wie ein generischer Code-Generator.
Mit dem richtigen Kontext und den Regeln kann Copilot automatisch:
- Große Teile von Cypress-Tests in Playwright-Syntax umschreiben
- Ordner- und Teststruktur konsistent halten
- Die manuelle Umwandlungszeit um 70–80 % reduzieren
Im Wesentlichen erstellen Sie Ihre eigene KI-Migrationswerkzeugkette — maßgeschneidert für die Struktur und Konventionen Ihres Projekts.
4. Wählen Sie den Agentenmodus und führen Sie die Migration Schritt für Schritt durch
Sobald die Aufforderung bereit war, bestand der nächste Schritt darin, die Migration tatsächlich im Agentenmodus mit GitHub Copilot durchzuführen. In dieser Phase ließen wir das KI-Modell jede Datei — Page Objects, Komponenten, Helfer und schließlich die Tests — nacheinander verarbeiten.
Für diese Aufgabe haben wir das Modell GPT-4.1 verwendet, das die konsistentesten und kontextbewusstesten Code-Transformationen liefert.
So starten Sie die Migration
1. Öffnen Sie Copilot Chat in VS Code
2. Verwenden Sie /, um die zuvor erstellte Eingabedatei auszuwählen
3. Verwenden Sie #, um eine bestimmte Datei auszuwählen (zum Beispiel einen Cypress-Test oder ein Seitenobjekt), das Sie migrieren möchten
Copilot wird nun Ihre Migrationsaufforderung anwenden und eine Playwright-kompatible Version der Datei direkt im Chat generieren.
Beispiel für die Migration des inventoryPage Seitenobjekts
import { ItemComponent } from "../components/item.component";
import Common from "./common";
class InventoryPage extends Common {
item = new ItemComponent();
backbackItem = () => cy.get("#item_4_title_link");
backbackAddItemButton = () => cy.getByTestId("add-to-cart-sauce-labs-backpack");
bikeLightsAddItemButton = () => cy.getByTestId("add-to-cart-sauce-labs-bike-light");
backbackRemoveItemButton = () => cy.getByTestId("remove-sauce-labs-backpack");
shoppingCartLogo = () => cy.get("#shopping_cart_container");
clickBackbackAddItemButton = () => this.backbackAddItemButton().click();
clickBikeLightsAddItemButton = () => this.bikeLightsAddItemButton().click();
clickBackbackRemoveItemButton = () => this.backbackRemoveItemButton().click();
assertCartLogoItems = (itemsNum: number) => this.shoppingCartLogo().should("have.text", itemsNum);
}
export default new InventoryPage();
import { Page, Locator, expect } from "@playwright/test";
import Common from "./common";
class InventoryPage extends Common {
constructor(page: Page) {
super(page);
this.item = new ItemComponent(page);
}
// Locators
backbackItem(): Locator {
return this.page.locator("#item_4_title_link");
}
backbackAddItemButton(): Locator {
return this.page.getByTestId("add-to-cart-sauce-labs-backpack");
}
bikeLightsAddItemButton(): Locator {
return this.page.getByTestId("add-to-cart-sauce-labs-bike-light");
}
backbackRemoveItemButton(): Locator {
return this.page.getByTestId("remove-sauce-labs-backpack");
}
shoppingCartLogo(): Locator {
return this.page.locator("#shopping_cart_container");
}
// Actions
async clickBackbackAddItemButton(): Promise<void> {
await this.backbackAddItemButton().click();
}
async clickBikeLightsAddItemButton(): Promise<void> {
await this.bikeLightsAddItemButton().click();
}
async clickBackbackRemoveItemButton(): Promise<void> {
await this.backbackRemoveItemButton().click();
}
async assertCartLogoItems(itemsNum: number): Promise<void> {
await expect(this.shoppingCartLogo()).toHaveText(String(itemsNum));
}
}
export default InventoryPage;Verhalten des Agents anpassen
Keine Sorge, wenn die erste Ausgabe nicht perfekt ist. Unserer Erfahrung nach bekommt der KI-Agent beim ersten Versuch normalerweise 60–70 % des Codes richtig hin. Der Schlüssel ist, den Gesprächskontext aktiv zu halten — verfeinern Sie das Ergebnis im selben Chatverlauf weiter, indem Sie nach Korrekturen oder Anpassungen fragen. Dabei beginnt Copilot, Ihre Projektmuster zu lernen, und liefert genaueren, wiederverwendbaren Code.
Im Wesentlichen gilt: Je mehr Iterationen Sie im selben Chat durchführen, desto besser wird die Qualität der Migration.
Migrationsreihenfolge: Zuerst Dateien, dann Tests
Um Abhängigkeiten konsistent zu halten und fehlende Referenzen zu vermeiden, befolgen Sie diese Reihenfolge:
1. Generieren Sie zuerst die Nicht-Testdateien:
- Page Objects
- Komponenten
- Hilfsdateien
- Konstanten
2. Sobald alle unterstützenden Dateien generiert und überprüft sind, fügen Sie den Ordner #app zum Chatkontext hinzu und migrieren Sie dann die Testdateien selbst.
Überprüfungsschritt
Wenn ein Playwright-Test generiert wurde:
1. Überprüfen Sie den Code — stellen Sie sicher, dass Seiten-Locatoren, Testschritte und Assertions korrekt sind.
2. Führen Sie den Test mit Ihrer Playwright-CLI aus, um sicherzustellen, dass er ordnungsgemäß läuft.
3. Beheben Sie kleinere Syntax- oder Fixture-Unstimmigkeiten, falls Copilot einen Cypress-Befehl missverstanden hat.
Wenn Sie nach jeder Datei prüfen, bleibt die Migration stabil und schrittweise, und Sie vermeiden später ein großflächiges Debugging.
Beispiel eines migrierten inventoryPage-Tests, der erfolgreich besteht:
import inventoryPage from "cypress/app/pageobjects/inventoryPage";
import loginPage from "cypress/app/pageobjects/loginPage";
import { itemsNames } from "../../fixtures/data.json";
describe("InventoryPage tests", () => {
beforeEach(() => {
loginPage.loginWithValidData();
});
it("The user should add item to the card", () => {
inventoryPage.item.itemByName("Bike Light").should("have.text", itemsNames.bikeLight);
inventoryPage.item.addToCartByName("Bike Light");
inventoryPage.assertCartLogoItems(1);
});
it("The user should remove item from the card", () => {
inventoryPage.backbackItem().should("have.text", itemsNames.backpackItemName);
inventoryPage.clickBackbackAddItemButton();
inventoryPage.assertCartLogoItems(1);
inventoryPage.clickBackbackRemoveItemButton();
inventoryPage.shoppingCartLogo().should("not.have.text");
});
it("The user should add multiple items to the card", () => {
inventoryPage.backbackItem().should("have.text", itemsNames.backpackItemName);
inventoryPage.clickBackbackAddItemButton();
inventoryPage.assertCartLogoItems(1);
inventoryPage.clickBikeLightsAddItemButton();
inventoryPage.assertCartLogoItems(2);
});
});
import { test, expect } from "../fixtures/test.fixture";
import { itemsNames } from "../../constants/data.json";
// InventoryPage tests migrated from Cypress to Playwright
test.describe("InventoryPage tests", () => {
test.beforeEach(async ({ pageManager }) => {
await pageManager.loginPage.loginWithValidData();
});
test("The user should add item to the cart", async ({ pageManager }) => {
await expect(
pageManager.inventoryPage.item.itemByName(itemsNames.bikeLight)).toHaveText(itemsNames.bikeLight);
await pageManager.inventoryPage.item.addToCartByName(itemsNames.bikeLight);
await pageManager.inventoryPage.assertCartLogoItems(1);
});
test("The user should remove item from the cart", async ({ pageManager }) => {
await expect(
pageManager.inventoryPage.backbackItem()).toHaveText(itemsNames.backpackItemName);
await pageManager.inventoryPage.clickBackbackAddItemButton();
await pageManager.inventoryPage.assertCartLogoItems(1);
await pageManager.inventoryPage.clickBackbackRemoveItemButton();
await expect(pageManager.inventoryPage.shoppingCartLogo()).not.toHaveText(/\d/); // Should not have any number text
});
test("The user should add multiple items to the cart", async ({ pageManager }) => {
await expect(pageManager.inventoryPage.backbackItem()).toHaveText(itemsNames.backpackItemName);
await pageManager.inventoryPage.clickBackbackAddItemButton();
await pageManager.inventoryPage.assertCartLogoItems(1);
await pageManager.inventoryPage.clickBikeLightsAddItemButton();
await pageManager.inventoryPage.assertCartLogoItems(2);
});
});5. Endgültige Repository-Struktur nach der Migration
Nach Abschluss der Migration erreichte unser Repository einen stabilen Zustand mit zwei Frameworks — Cypress und Playwright liefen vollständig nebeneinander. Diese Struktur ermöglichte es uns:
- Cypress-Test-Suiten schrittweise abzulösen, sobald sie ersetzt waren
- CI/CD-Pipelines für beide Frameworks betriebsbereit zu halten
- Eine saubere und modulare Architektur aufrechtzuerhalten
Nachfolgend die endgültige Struktur nach Abschluss des Migrations-Workflows.
Endgültige Ordnerstruktur
├── .env
├── .gitignore
├── readme.md
├── package.json
├── playwright.config.ts
├── smoke.config.ts
├── tsconfig.cypress.json
├── tsconfig.playwright.json
├── tsconfig.json
├── .github/
│ ├── prompts/
│ └── workflows/
├── cypress/
│ ├── app/
│ ├── downloads/
│ ├── e2e/
│ ├── fixtures/
│ ├── screenshots/
│ ├── support/
│ └── videos/
├── playwright/
│ ├── app/
│ ├── constants/
│ ├── helpers/
│ ├── reports/
│ ├── test-results/
│ └── tests/
Hinweise zur Struktur
.github/prompts/— enthält Ihre Copilot-Migrations-Prompts. Wenn Sie diese versioniert halten, können Sie sie für zukünftige Framework-Migrationen oder Refactorings aktualisieren oder wiederverwenden..github/workflows/— enthält Ihre CI/CD-Definitionen. Sie können Playwright- und Cypress-Jobs parallel ausführen, bis das alte Framework vollständig abgelöst ist.- Ordner
playwright/— fungiert ab jetzt als primäre Automatisierungsquelle. Er spiegelt die Struktur der bisherigen Cypress-Implementierung wider, um die Einarbeitung zu erleichtern und Konsistenz sicherzustellen. tsconfig.playwright.jsonundtsconfig.cypress.json— bleiben für eine vollständige Typisolierung getrennt und verhindern Konflikte bei Builds.
Diese Struktur unterstützte nicht nur einen reibungslosen Übergang, sondern bereitete das Projekt auch auf zukünftige Skalierbarkeit, CI-Flexibilität und eine modulare Testverantwortung über Teams hinweg vor.
Migrieren Sie Ihre Test-Suite, ohne Releases zu stören oder Testabdeckung zu verlieren. Unsere QA-Ingenieure helfen Ihnen, die Migration zu planen, Ihre Automatisierungsarchitektur anzupassen und beide Frameworks während des Übergangs stabil zu halten.
Praktische Ratschläge & Erkenntnisse
Nach der Migration von fast 150 Tests mit Copilot und Playwright hier einige wichtige Lektionen und praxisnahe Tipps, die den Prozess reibungsloser gemacht (und uns einige Kopfschmerzen erspart) haben.
Allgemeine Ratschläge zu KI
- KI lügt — oft überzeugend.
Überprüfen Sie immer jede Codezeile, die die KI generiert. Vertrauen Sie ihr niemals blind, insbesondere bei Infrastrukturmigrationen.
- Halten Sie sich an konsistente Namenskonventionen.
Verwenden Sie in beiden Frameworks identische Variablen- und Methodennamen — das hilft dem KI-Agenten, Code genauer zu migrieren, und erleichtert es Ihnen, sie später zu finden und zu importieren.

- Importe manuell neu aufbauen.
Copilot neigt dazu, Importpfade durcheinanderzubringen. Oft ist es schneller, sie zu löschen und manuell neu zu importieren oder globale Importe einzurichten, etwa: import x from "playwright/foo/bar";
- Verwenden Sie denselben Chat für alle Migrationen.
Bleiben Sie in einem einzigen Copilot-Chatverlauf — so baut sich Kontext auf, Copilot „merkt“ sich die Konventionen Ihres Projekts, und die Ergebnisse werden besser.
- Teilen Sie Migrationsaufgaben auf.
Generieren Sie 1–2 Dateien auf einmal. Kleinere Aufgaben führen zu genaueren Ergebnissen und erleichtern die manuelle Validierung.
Playwright-spezifische Tipps
- Erstellen Sie niemals ohne Notwendigkeit mehrere Instanzen desselben Page Objects.
Das kann beim Navigieren zwischen Seiten zu inkonsistentem Zustand oder defekten Selektoren führen. Die Lösung? Implementieren Sie einen Page Manager, der alle Page Objects über eine einzige Instanz zugänglich macht.
Hier ist ein minimales Beispiel-Setup:
// login.page.ts
class LoginPage extends Common {
async clickLoginButton(): Promise<void> {
await this.loginButton().click();
}
}
export default LoginPage;
//-----------------------------------------------------------
//PageManager.ts
export class PageManager {
readonly loginPage: LoginPage;
constructor(page: Page) {
this.loginPage = new LoginPage(page);
}
}
//-----------------------------------------------------------
//test.fixture.ts
export const test = base.extend<{
pageManager: PageManager;
}>({
pageManager: async ({ page }, use) => {
const manager = new PageManager(page);
await use(manager);
},
});
export { expect };
//-----------------------------------------------------------
//userLogin.spec.ts
test("The user should login with valid data", async ({ pageManager }) => {
await pageManager.loginPage.clickLoginButton();
});Weitere technische Tipps
- Entfernen Sie alle „get“-Präfixe aus den Locatoren, die der KI-Agent generiert — verwenden Sie stattdessen direkte Locator-Methoden.
- Vergessen Sie nicht,
dotenvfür Umgebungsvariablen und Zugangsdaten zu konfigurieren. - Passen Sie Ihre Playwright-Konfiguration an, sodass alle Berichte im Playwright-Ordner landen und Ihr Projektstamm sauber bleibt:
reporter: [["html", { open: "never", outputFolder: "./playwright/reports/" }]],
outputDir: "./playwright/test-results",
Nachwort
Die ESLint-Konfiguration habe ich hier nicht aufgenommen, da sie von Projekt zu Projekt variiert.
Die einzige universelle Empfehlung, die ich geben kann, ist, für eine bessere Regeldurchsetzung das Playwright-ESLint-Plugin zu installieren: eslint-plugin-playwright.
Wenn Sie ein vollständiges, funktionierendes Beispiel dieser Migration erkunden möchten — einschließlich Prompt-Dateien, tsconfigs und CI-Setup —, finden Sie das vollständige Repository hier: Repo-Beispiel.
Ob Sie von Cypress migrieren, eine instabile Test-Suite neu aufbauen oder Ihre QA-Automatisierung verbessern möchten — JetBase hilft Ihnen, den Übergang mit weniger manueller Arbeit und geringerem Lieferrisiko zu gestalten.















