//

JavaScript UI Tests mit Struktur

27.2.2018 | 3 Minuten Lesezeit

Mit einem guten Freund streite ich mich immer wieder über das Thema Tests in Web-Frontends. In der Regel vertauschen sich unsere Positionen – je nachdem, wie frustriert derjenige ist, der aktuell Tests schreibt.
Wir sprechen heute meistens eher von Web-Anwendungen als von Web-Frontends. Der Code dazu ist entsprechend umfangreich, obwohl moderne Web-Frameworks wie Angular, React oder Vue dem Entwickler viel Tipparbeit abnehmen.
Um die Funktionalität der Anwendung nach Änderungen sicherzustellen, hilft eine gute Testabdeckung. Doch wenn nach jeder kleinen Änderung die Tests angepasst werden müssen, ist man schnell genervt von der Menge nötiger Änderungen an den Tests. Das muss nicht sein! In diesem Artikel möchte ich zeigen, dass man mit ein wenig Strategie und Struktur beim Schreiben von Tests eine Menge Frust vermeiden kann.

Setup

Zu Demonstrationszwecken erstellen wir eine einfache Vue-Anwendung. Keine Sorge, es werden keine Vue-Kenntnisse vorausgesetzt. Die vorgestellte Strategie ist universell für alle gängigen UI- und Test-Frameworks übertragbar. Das Projekt wird erzeugt mit

1# npm i -g vue-cli
2# vue init webpack .
3 
4? Generate project in current directory? Yes
5? Project name better-tests
6? Project description Tutorial: Bessere JS Tests schreiben
7? Author Author <author@example.com>
8? Vue build standalone
9? Install vue-router? No
10? Use ESLint to lint your code? Yes
11? Pick an ESLint preset Standard
12? Set up unit tests Yes
13? Pick a test runner jest
14? Setup e2e tests with Nightwatch? No
15? Should we run `npm install` for you after the project has been created? (recommended) npm
16

Der Standard-Test

Mit diesen Einstellungen erzeugt uns vue-cli eine lauffähige Anwendung und einen Test für die derzeit einzige Komponente im Projekt. Der Test befindet sich in test/unit/specs/HelloWorld.spec.js und sieht so aus:

1import Vue from 'vue'
2import HelloWorld from '@/components/HelloWorld'
3 
4describe('HelloWorld.vue', () => {
5  it('should render correct contents', () => {
6    const Constructor = Vue.extend(HelloWorld)
7    const vm = new Constructor().$mount()
8    expect(vm.$el.querySelector('.hello h1').textContent)
9      .toEqual('Welcome to Your Vue.js App')
10  })
11})
12

Für eine „Hello World“-Komponente ist das absolut in Ordnung. Unschön ist allerdings, dass im Test mit Selektoren gearbeitet wird. Falls aus Stilgründen die Überschrift in ein anderes Tag verschoben wird oder man die CSS-Klasse umbenennt, wird der Test fehlschlagen. In solchen Fällen muss man in der gesamten Test-Spezifikation alle verwaisten Selektoren korrigieren. In einer „Hello World“-Komponente mit genau einem Test ist das überschaubar. Selten findet man in produktivem Code derart einfache Komponente.

Die Idee

Ich empfehle eine Trennung zwischen Testlogik und der Logik für die Konstruktion und Beschreibung der aktuellen Ansicht. Konkret kann man das obige Beispiel wie folgt umschreiben:

1import Vue from 'vue'
2import HelloWorld from '@/components/HelloWorld'
3 
4class HelloWorldPage {
5  constructor () {
6    // zu testenden View erzeugen
7    const Constructor = Vue.extend(HelloWorld)
8    this.vm = new Constructor().$mount()
9  }
10 
11  headline = () => this.vm.$el.querySelector('.hello h1').textContent; // Selektor für Überschrift
12}
13 
14describe('HelloWorld.vue', () => {
15  it('should render correct contents', () => {
16    // enthält keine View spezifische Logik
17    const view = new HelloWorldPage()
18    expect(view.headline()).toEqual('Welcome to Your Vue.js App')
19  })
20})
21

Das Konstruieren des Views und die Selektoren sind aus dem Test verschwunden. Der Test enthält nur noch die tatsächliche Testlogik. Geht man noch einen Schritt weiter, verschiebt man die neue HelloWorldPage-Klasse in eine eigene Datei und importiert diese in der Test-Spezifikation. Auf diese Weise bereinigt man Tests vollständig von der Framework-Logik. Man könnte den Test für die gleiche Komponente verwenden, selbst wenn diese mit einem anderen Framework umgesetzt wird.

Fazit

Der wesentliche Vorteil dieses Vorgehens ist, dass Änderungen in der Präsentation, also dem HTML Markup, nur in der Zwischenschicht nachgezogen werden müssen. Die Testlogik bleibt unverändert, da sich durch Änderungen am Markup keine Logik ändert.

Beitrag teilen

Gefällt mir

0

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen

Wir helfen Deinem Unternehmen

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.