Debugging von Cypress-CI Netzwerkkommunikation
Um Anwendungen sicher und verlässlich (weiter-)entwickeln zu können, ist es etablierte Praxis, Tests zu schreiben, die das Verhalten der Anwendung sicher nachstellen und überprüfen. Unterschieden wird dabei gerne zwischen feiner-granularen, eher technisch gelagerten Tests, wie den Unit-Tests, und Tests, die die Anwendung aus User-Sicht testen sollen. Das wird etwa mit Ende-zu-Ende oder auch E2E-Tests erreicht.
Jedoch kann es auch bei den Tests selbst zu spezifischen Problemen kommen, die ggf. nur innerhalb der Test-Umgebung oder sogar nur in einer speziellen Umgebung wie der CI-Pipeline auftauchen. So kann es beispielsweise zu Test-Fehlern kommen, wenn in der CI-Umgebung eine andere Authentifizierungsinfrastruktur, z.B. andere Client-Zertifikate, als in den restlichen Systemen verwendet wird.
CI-Prozesse laufen in der Regel auf separaten CI-Servern und in einer isolierten Umgebung, wie einer Container-Laufzeit, z.B. in Docker.
Treten in dieser Umgebung nun Netzwerk-Fehler auf, gestaltet sich die Diagnose häufig aufwendig, da die Details der Netzwerk-Kommunikation nicht direkt einsehbar sind.
Das wollen wir uns in diesem Artikel anhand einer Angular-Anwendung mit Cypress E2E-Tests verdeutlichen.
Zur besseren Untersuchung der Netzwerk-Kommunikation werden wir dann einzelne Requests innerhalb der E2E- bzw. Integrations-Tests in HAR-Dateien protokollieren.
Dies wird automatisiert durch ein Cypress-Plugin realisiert.
Die HAR-Dateien können dann auch in der CI-Umgebungen geschrieben und zur anschließenden Diagnose verwendet werden.
Zunächst setzen wir dazu ein neues Angular-Projekt (Projektname cypress-har
) auf und fügen Cypress-E2E-Tests hinzu, wie in folgendem Listing dargestellt ist.
$ npm i -g @angular/cli@latest
$ ng new cypress-har --style=scss --ssr=false
$ cd cypress-har
$ ng add @cypress/schematic --e2e --skip-confirmation --component=false
Damit Cypress die HAR-Dateien aus den Tests heraus generieren kann, wird das Cypress-Plugin cypress-har-generator
verwendet.
Im folgenden Listing ist der Befehl zur Installation des Paketes per npm gezeigt.
$ npm i -D @neuralegion/cypress-har-generator
Damit der HAR-Generator funktioniert, muss er noch in der Cypress-Konfiguration berücksichtigt werden.
Dafür muss in der Environment das Property “hars_folders” gesetzt werden, siehe dazu folgendes Listing.
Danach kann das Plugin aktiviert werden, dies geschieht innerhalb des setupNodeEvents
-Hook, indem die install()
-Funktion aufgerufen wird.
Aufgrund von Cypress-Issue 5240 muss zusätzlich die Funktion ensureBrowserFlags()
als Teil des “before:browser:launch”-Event-Handler aufgerufen werden.
cypress.config.ts
import { ensureBrowserFlags, install } from '@neuralegion/cypress-har-generator';
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
'baseUrl': 'http://localhost:4200',
env: {
"hars_folders": "cypress/hars"
},
setupNodeEvents(on) {
install(on);
on('before:browser:launch', (browser, launchOptions) => {
ensureBrowserFlags(browser, launchOptions);
return launchOptions;
});
}
}
});
Der HAR-Generator muss in den Tests über eigene Cypress-Kommandos aufgerufen werden.
Die Kommandos müssen dafür einmal importiert werden.
Das wird in der Datei cypress/support/e2e.ts
(in älteren Cypress-Versionen die cypress/support/index.ts
), wie in folgendem Listing gezeigt, umgesetzt.
cypress/support/e2e.ts
import '@neuralegion/cypress-har-generator/commands';
Für einen besseren TypeScript-Support (bspw. in Webstorm) sollten auch noch die HAR-Generator-Typinformationen innerhalb der cypress/tsconfig.ts
zu den "types"
hinzugefügt werden, wie auch folgendes Listing zeigt.
cypress/tsconfig.ts
einbinden{
"extends": "../tsconfig.json",
"include": ["**/*.ts"],
"compilerOptions": {
"sourceMap": false,
"types": ["cypress", "@neuralegion/cypress-har-generator"]
}
}
Um auch tatsächliche Netzwerk-Requests protokollieren zu können, müssen wir natürlich auch einen Netzwerk-Request zu unserer Anwendung hinzufügen.
Dazu legen wir in unserem Projekt eine Datei mit Demo-Daten ab.
Je nachdem, ob man das Projekt mit einer älteren oder einer etwas neueren Version von Angular-CLI aufgesetzt hat, muss die Datei im Ordner public/
(neu) oder src/assets/
(alt) abgelegt werden.
Um die beiden Varianten zu vereinheitlichen, legen wir die Datei in diesem Beispiel unter dem Pfad public/assets/data
ab.
public/assets/data
bzw. in älteren Angular-CLI-Projekten in src/assets/data
{
"data": [ "test", "foo", "bar" ]
}
Die so abgelegten Daten können wir dann per Angular-HttpClient
in der App abfragen.
Dazu legen wir einen DemoService
an, der eine GET-Abfrage gegen den Endpunkt 'assets/data'
durchführt, siehe folgendes Listing.
assets/data
@Injectable({ providedIn: 'root' })
export class DemoService {
constructor(private readonly http: HttpClient) { }
getData(): Observable<{ data: string[] }> {
return this.http.get<{ data: string[] }>('assets/data');
}
}
Der DemoService
wird dann per Angular-Dependency Injection in der DemoComponent
zur Verfügung gestellt.
Durch Aufruf der getData()
-Methode werden dann die Daten ausgelesen und in der Komponente angezeigt.
Zur Anzeige im Template wird die neue, seit Angular 18 als stabil markierte Control Flow Syntax verwendet, wie im folgendenden Listing auch zu sehen ist.
@Component({
selector: 'app-demo',
standalone: true,
template: `
<p>Demo-Data:</p>
@if (dataContainer|async; as container) {
<div data-e2e="demo-data">
@for (entry of container.data; track entry) {
<div>{{ entry }}</div>
}
</div>
}
`,
imports: [AsyncPipe]
})
export class DemoComponent {
readonly dataContainer: Observable<{ data: string[] }>;
constructor(demoService: DemoService) {
this.dataContainer = demoService.getData();
}
}
Für die so erzeugte Anwendung wird nun ein einfacher Cypress E2E-Test geschrieben.
Im Test wird die Seite per cy.visit()
aufgerufen.
Um auch sicherzustellen, dass die Demo-Daten erfolgreich geladen wurden, wird noch überprüft, ob der Text 'testfoobar'
auf der Seite vorhanden ist.
Falls im Testablauf Fehler auftreten, werden diese von Cypress innerhalb von CI-Umgebungen typischerweise per Video und Screenshot protokolliert. Wenn aber beim Abruf der Demo-Daten ein Netzwerkfehler auftritt, ist der dahinter liegende Fehler nicht immer ganz einfach zu diagnostizieren, da Detail-Informationen zu Netzwerk-Requests typischerweise nicht Bestandteil der Videos und Screenshots ist.
describe('My HAR-Demo Test', () => {
it('loads the the app including all demo-data', () => {
cy.visit('/');
cy.get('[data-e2e="demo-data"]').should('include.text', 'testfoobar');
});
});
Um in der CI-Umgebung nun detailliertere Informationen über die Netzwerk-Requests zu erhalten, setzen wir den HAR-Recorder ein.
Der wird zunächst im before
-Hook durch Aufruf von cy.recordHar()
aktiviert.
Nach Durchlauf der Tests können die erfolgten Requests dann in einer .har
-Datei gespeichert werden.
Das erledigt die cy.saveHar()
-Funktion, hier beispielhaft im after
-Test-Hook von Cypress aufgerufen.
Wird das Property fileName
weggelassen, so wird standardmäßig der Dateiname der Test-Datei als Grundlage für den Namen der HAR-Datei genommen.
describe('My HAR-Demo Test', () => {
before(() => {
// start recording
cy.recordHar();
});
after(() => {
// save the HAR file
cy.saveHar({fileName: 'important-connections.har'});
});
it('loads the the app including all demo-data', () => {
cy.visit('/');
cy.get('[data-e2e="demo-data"]').should('include.text', 'testfoobar');
});
});
Es ist zu beachten, dass das HAR-Plugin momentan nur mit Chrome-basierten Browsern funktioniert, also z.B. Chrome selbst oder Microsoft Edge.
Damit der HAR-Generator arbeiten kann, muss zudem der Remote-Debugging-Port des Browsers aktiviert sein.
Das kann durch den Kommandozeilen-Parameter --remote-debugging-port=9222
von Chrome erreicht werden.
Damit der Parameter durch Cypress beim Aufruf des Browsers auch gesetzt wird, kann der Environment-Parameter ELECTRON_EXTRA_LAUNCH_ARGS
beim Aufruf von ng e2e
genutzt werden, siehe folgendes Listing.
$ ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=9222 ng e2e
Im Verlauf der Tests sollte im Cypress-Test-Log zu sehen sein, dass das HAR-Plugin aktiviert ist und die HAR-Datei geschrieben wurde. Die erwartete Ausageben ist im nebenstehenden Screenshot zu sehen.
Die durch den Test generierte HAR-Datei kann dann verwendet werden, um Detail-Informationen zu den einzelnen Netzwerk-Requests auszulesen. Dazu gibt es spezielle HAR-Analyse-Tools. Als Beispiel wird hier der HAR-Analyzer aus der Google-Toolbox verwendet, siehe:
Dort können dann beispielsweise die Header von Request und Response, verwendete (Query-)Parameter, Timing-Informationen, Cookies und einige Informationen mehr zum Request dargestellt werden. Der Request für unsere Demo-Daten ist in folgendem Screenshot beispielhaft hervorgehoben.
Mit diesem Plugin und etwas Konfiguration ist es nun möglich auch bei schwer durch die Oberfläche zu diagnostizierenden Fehlern anhand der Netzwerkdaten von Tests in der CI-Pipeline eine Analyse durchzuführen
Zu den Themen Angular und Webanwendungen bieten wir sowohl Schulungen, Entwicklungsunterstützung als auch Beratung an:
Auch für Ihren individuellen Bedarf bieten wir gerne angepasste Workshops, Consulting und Schulungen an.
Sprechen Sie uns unverbindlich an.