Generierung von automatisierten Tests mit Esprima

Der JavaScript-Parser Esprima kann bei der Generierung von Unit-Test-Codegerüsten helfen und dadurch Entwicklern die Arbeit vereinfachen.

In Pocket speichern vorlesen Druckansicht
Generierung von automatisierten Tests mit Esprima

(Bild: geralt, PIxabay)

Lesezeit: 15 Min.
Von
  • Philip Ackermann
Inhaltsverzeichnis

Die automatische Generierung von Code kann Entwicklern eine Menge an Arbeit sparen. Dieser Artikel zeigt, wie sich mit dem JavaScript-Parser Esprima auf Basis von JavaScript-Klassen entsprechende Unit-Test-Codegerüste generieren lassen.

Es gibt kaum einen Anwendungsfall, für den es mittlerweile kein Node.js-Paket gibt. Das gilt auch für das Parsen von Datenformaten. Für die XML- und HTML-Verarbeitung gibt es beispielsweise cheerio, für das Parsen von Markdown das Paket marked, und JSON lässt sich ohnehin nativ in JavaScript verarbeiten.

Auch für das Parsen von JavaScript stehen verschiedene Bibliotheken zur Verfügung – zum Beispiel das im folgenden vorgestellte Esprima, das im Vergleich zu anderen Parser-Bibliotheken wie acorn, espree oder shift, aber auch im Vergleich zu den Parsern von UglifyJS2 und Google Traceur deutlich performanter ist (siehe Benchmarking unter Esprima Benchmark).

Esprima kann sowohl im Browser als auch unter Node.js verwendet werden. Die Installation erfolgt wie gewohnt über den Node.js Package Manager (npm) mit dem Befehl

npm install esprima

Das anschließende Einbinden in die eigene Applikation erfolgt über

require('esprima')

Esprima eignet sich sowohl für die lexikalische Analyse als auch für die syntaktische Analyse. Zur Erinnerung: Bei ersterer wird der zu parsende Quelltext von einem Tokenizer in einzelne Tokens unterteilt und Schlüsselwörter, Werte, Operatoren und so weiter ermittelt. Bei letzterer wird überprüft, ob die Tokens den syntaktischen Regeln der jeweiligen Sprache entsprechen. Für die lexikalische Analyse stellt Esprima die Methode tokenize() zur Verfügung. Der als Zeichenkette übergebene JavaScript-Code spaltet sich, wie in folgendem Listing zu sehen, in einzelne Tokens auf, wobei jedem Token der jeweilige Typ zugeordnet wird:

const esprima = require('esprima');
const input = 'const name = "Max Mustermann"';

const tokens = esprima.tokenize(input);
console.log(tokens);

/*
[ { type: 'Keyword', value: 'const' },
{ type: 'Identifier', value: 'name' },
{ type: 'Punctuator', value: '=' },
{ type: 'String', value: '"Max Mustermann"' } ]
*/

Für die syntaktische Analyse dagegen greift man auf die Methode parse() zurück. Als Parameter übergibt man wieder den JavaScript-Code in Form einer Zeichenkette, erhält als Rückgabewert aber einen abstrakten Syntaxbaum (AST) in Form eines JavaScript-Objekts. Das folgende Listing zeigt beispielhaft den Code, der notwendig ist, um die Javascript-Anweisung const name = "Max Mustermann" zu parsen:

const esprima = require('esprima');
const input = 'const name = "Max Mustermann"';
const ast = esprima.parse(input);
console.log(JSON.stringify(ast, null, 2));

/*
Program {
type: 'Program',
body:
[ VariableDeclaration {
type: 'VariableDeclaration',
declarations: [Array],
kind: 'const' } ],
sourceType: 'script' }
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "name"
},
"init": {
"type": "Literal",
"value": "Max Mustermann",
"raw": "\"Max Mustermann\""
}
}
],
"kind": "const"
}
],
"sourceType": "script"
}
*/

Da es sich beim abstrakten Syntaxbaum um eine einfache JSON-Struktur handelt, können Entwickler ihn nach Belieben verändern. Der Phantasie sind nahezu kein Grenzen gesetzt, vorausgesetzt, man folgt den syntaktischen Regeln von JavaScript. Im gegebenen Codebeispiel kann man relativ einfach die Variable name in fullname umbenennen. Hart kodiert könnte das so aussehen:

...
ast.body[0].declarations[0].id.name = 'fullname';
console.log(JSON.stringify(ast, null, 2));

/*
...
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "fullname"
},
"init": {
"type": "Literal",
"value": "Max Mustermann",
"raw": "\"Max Mustermann\""
}
}
],
...
*/

Den geänderten Syntaxbaum können Anwender nun wieder in JavaScript-Code umwandeln. Das kann man entweder händisch erledigen (indem man den gesamten Syntaxbaum durchwandert und daraus wieder JavaScript-Code zusammenbaut) oder auf spezielle Bibliotheken zurückgreifen, die mit dem Syntaxbaum-Format von Esprima umgehen und es in JavaScript-Code umwandeln können (Esprima bietet dazu leider keine Möglichkeit). Ein Beispiel hierfür ist die Bibliothek Escodegen die ebenfalls als Node.js-Paket zur Verfügung steht.

Nach Einbinden des Pakets über

const escodegen = require('escodegen');

übergibt man der Methode generate() den abstrakten Syntaxbaum und erhält als Rückgabewert den passenden JavaScript-Code:

const esprima = require('esprima');
const escodegen = require('escodegen');
// ...
const code = escodegen.generate(ast);
console.log(code);
// Ausgabe:
// const fullname = 'Max Mustermann';