Web3-dApp-Tests mit Synpress
Die wichtigste Regel in Web3: Die UI ist nicht die Quelle der Wahrheit, die Blockchain ist es. Eine dApp kann "Erfolg" für eine Transaktion anzeigen, die rückgängig gemacht wurde, ersetzt wurde oder die Wallet nie verlassen hat. Jeder Test, der den Status ändert, endet mit einer On-Chain-Überprüfung.
Einrichtung
Überprüfen Sie zuerst, was das Projekt verwendet. Diese Notizen beziehen sich auf Synpress v4 (Playwright-basiert). v3 basierte auf Cypress und seine API ist anders.
npm i -D @synthetixio/synpress @playwright/test viem
Verwenden Sie "type": "module" in package.json und "moduleResolution": "Bundler" in tsconfig.json (siehe examples/tsconfig.json); Synpress liefert ES-Module aus. Installieren Sie die @playwright/test-Version, von der Synpress abhängt (npm ls @playwright/test), sonst erhalten Sie zwei Playwright-Kopien mit inkompatiblen Typen.
Starten Sie eine lokale Chain, damit Tests schnell, kostenlos und wiederholbar sind. Foundrys Anvil ist die übliche Wahl:
anvil --chain-id 31337 --block-time 1 # 10 finanzierte Konten aus der Standard-Test-Mnemonic
# Oder forken Sie ein echtes Netzwerk bei einem festen Block, sodass der Vertragsstatus bei jedem Lauf gleich ist:
anvil --fork-url $RPC_URL --fork-block-number 21000000
Verwenden Sie niemals eine Seed-Phrase, die jemals echte Gelder enthalten hat. Verwenden Sie die Anvil-Test-Mnemonic oder ein dediziertes Wegwerf-Wallet, das in Umgebungsvariablen gespeichert und niemals committet wird.
Wallet-Einrichtung und Cache
Synpress erstellt ein Browser-Profil mit bereits konfiguriertem MetaMask und cached es, sodass Tests das Wallet nicht jedes Mal neu importieren müssen. Siehe examples/wallet-setup/basic.setup.ts.
npx synpress # erstellt den Cache aus jeder *.setup.ts im wallet-setup-Ordner
Bauen Sie den Cache neu auf, wenn sich die Setup-Datei oder die MetaMask-Version ändert. In CI benötigt die Extension ein Display: Führen Sie Tests unter xvfb-run aus.
Was abgedeckt werden sollte
| Flow | Fälle, die echte Bugs finden |
|---|---|
| Verbinden | erste Verbindung, Verbindung ablehnen, trennen und wieder verbinden, Wallet gesperrt, falsches Konto ausgewählt |
| Netzwerk | dApp auf der falschen Chain, Benutzer wechselt die Chain während des Flows, Netzwerk hinzufügen, das der Wallet fehlt |
| Signieren (SIWE, Permits) | bestätigen, ablehnen, mit Konto A signieren, dann zu B wechseln vor dem Absenden |
| Transaktion | bestätigen, ablehnen, unzureichendes Guthaben, Gasspitze, ersetzte oder beschleunigte Tx, Rückgängigmachung durch den Vertrag |
| Token-Genehmigung | exakte vs. unbegrenzte Genehmigung, genehmigen dann ausgeben, ausgeben ohne Genehmigung |
| Ausstehender Zustand | neu laden während des Wartens, Tab schließen und zurückkommen, zwei Transaktionen hintereinander |
| Anzeige | Guthaben nach der Tx (UI vs. Chain), Dezimalstellen und Rundung, sehr große und Staubmengen |
Das Ablehnen in der Wallet ist genauso wichtig wie das Bestätigen. Viele dApps hängen nach einer Ablehnung für immer in "Warte auf Bestätigung" fest.
Schreiben eines Tests
Muster (vollständige Datei in examples/transfer.spec.ts):
- Lesen Sie den Startzustand von der Chain mit
viem(getBalance,readContract). - Steuern Sie die dApp-UI mit Playwright-Lokatoren und die Wallet mit dem
MetaMask-Helfer (connectToDapp,confirmSignature,confirmTransaction,rejectTransaction,switchNetwork). - Holen Sie sich den Transaktions-Hash aus der UI, einem Event oder der Netzwerkantwort, dann
waitForTransactionReceipt. - Bestätigen Sie den Empfangsstatus und den neuen On-Chain-Zustand. Bestätigen Sie dann, dass die UI zustimmt.
Halten Sie Wallet-unabhängige Logik (Betragsvalidierung, Formatierung, Fehlertexte) in schnelleren Tests mit Synpress' Ethereum-Wallet-Mock oder in einfachem Playwright mit einem gemockten Provider. Verwenden Sie echte MetaMask nur für die Flows, bei denen die Extension selbst wichtig ist.
Typische Bugs, auf die Sie achten sollten
- UI zeigt Erfolg, während der Empfangsstatus
revertedist, oder bevor der Empfang überhaupt existiert. - Guthaben in der UI ist gecached und aktualisiert sich nicht nach der Transaktion.
- Betragspräzision: 18-stellige Token mit Float-Rundung angezeigt; Max-Button hinterlässt Staub oder überschreitet das Guthaben um 1 Wei.
- Nach einem Chain-Wechsel ruft die dApp weiterhin die Vertragsadresse der alten Chain auf.
- Eine abgelehnte Signatur deaktiviert den Button bis zum Neuladen.
- Dieselbe Aktion kann zweimal gesendet werden, während die erste Transaktion aussteht.
- Governance: Stimme in der UI gezählt, aber der Vorschlagsstatus auf der Chain unverändert (falsche Vorschlags-ID, Stimme nach dem Frist-Block abgegeben).
Melden Sie einen Web3-Bug mit
Netzwerk und Chain-ID, Vertragsadresse, Wallet-Adresse (nur Test-Wallet), Transaktions-Hash und Blocknummer, Empfangsstatus, was die UI anzeigte vs. was die Chain zeigt, MetaMask-Version. Ein Block-Explorer-Link (oder Anvil-Trace mit cast run <txhash>) ist der beste Beweis.