<![CDATA[mocha - ]]>http://localhost:2368/Ghost 0.11Tue, 10 Oct 2017 14:25:12 GMT60<![CDATA[Leserliche require Anweisungen für mocha Unit Tests in Clean Architecture Node Applications]]>Bei Pulsar Solutions versuchen wir die Richtlinien von Clean Architecture zu befolgen, um Code zu produzieren, der testbar ist und unabhängig von Frameworks, UI, Datenbanken und sogar das Web. Gleichzeitig sollte der Zweck der Applikation offensichtlich werden, indem man sich die Dateistruktur ansieht. Das resultiert allerdings oft in einem weit

]]>
http://localhost:2368/2016/01/13/readable-require-statements-for-mocha-unit-tests-in-clean-architected-node-applications/50c71325-4fda-405b-956a-335b831c163bWed, 13 Jan 2016 21:33:17 GMTBei Pulsar Solutions versuchen wir die Richtlinien von Clean Architecture zu befolgen, um Code zu produzieren, der testbar ist und unabhängig von Frameworks, UI, Datenbanken und sogar das Web. Gleichzeitig sollte der Zweck der Applikation offensichtlich werden, indem man sich die Dateistruktur ansieht. Das resultiert allerdings oft in einem weit verzweigten Dateibaum, was zu unleserlichen und schwer wartbaren require Anweisungen führt.

In diesem Artikel liegt der Fokus darin, leserliche und wartbare require Anweisungen für die Unit Tests einer Anwendung zu ermöglichen. Der Einfachheit halber setze ich voraus, dass mocha als Testing Framework eingesetzt wird. Für andere beliebte Frameworks kann ein ähnlicher Ansatz verwendet werden.

Gehen wir von der folgenden Verzeichnisstruktur aus:

-- root
  -- node_modules
  -- src
    -- 0_externals
    -- 1_controllers_gateways_presenters
    -- 2_use_cases
      -- searchCats.js
    -- 3_entities
  -- tests
    -- ...
    -- 2_use_cases
      -- searchCatsTest.js
    -- ...
  -- ...

Dem entsprechend soll searchCatsTest.js den Code in searchCats.js testen. Mit mocha und der oben gezeigten Struktur würde die require Anweisung so aussehen:

var searchCats = require('../../../src/2_use_cases/searchCats');  

für einen Node Modul mit dieser Definition:

module.exports = searchCats;  

Das ist offensichtlich schlecht. Es ist unleserlich und, sollte sich die Verzeichnisstruktur aus irgend einem Grund nach einigen Monaten Entwicklung verändern, müssten alle require Anweisungen angepasst werden.

Wäre es nicht besser wenn wir schreiben könnten

var searchCats = require('2_use_cases/searchCats');  

und wir könnten das Root Verzeichnis global setzen? Leider funktioniert die o.g. Syntax nicht out-of-the-box, da require nicht weiss wo die Datei searchCats.js liegt.

Bevor wir weitermachen sollten wir einen Blick darauf werfen, wie mocha in der Kommandozeile aufgerufen wird:

mocha -R spec -u bdd ./tests  

Dieser Aufruf teilt mocha mit, dass alle Tests im Verzeichnis ./tests rekursiv auszuführen sind, dass der BDD Style verwendet werden soll und dass der Fortschritt im spec Format erfolgen soll. Da wir aber diesen Aufruf aus dem Root-Verzeichnis ausführen, überrascht es nicht, dass searchCats.js nicht gefunden wird. Wir kriegen eine Exception.

Googelt man ein wenig gelangt man schnell zu dieser interessanten Sammlung von Lösungen für ein ähnliches Problem aus der Node.js Welt: (Better local require() paths for Node.js) Ich bevorzuge Lösung Nummer 7, den globalen Wrapper um require. Aber wie können wir diese Technik auf unsere Mocha Tests anwenden? Klar ist, dass es nicht funktionieren wird, wenn mocha weiterhin mit dem o.g. Aufruf gestartet wird. Glücklicherweise kann man einen eigenen Mocha Runner als minimalistische Node.js Anwendung implementieren. Wie der Zufall so will hat das jemand bereits getan: (Starting Mocha Tests Programmically With Runner.js).

Alles was wir jetzt tun müssen, ist den Wrapper um require herum am Anfang des Runners einzubauen. Der Runner sieht dann so aus:

var fs = require('fs'),  
    Mocha = require("mocha"),
    path = require('path');

global.rootRequire = function (name) {  
    return require(__dirname + '/src/' + name);
}

// Our Mocha runner
var mocha = new Mocha({  
    ui: "bdd",
    reporter: "spec",
    timeout: 60000,
    slow: 10000
});

// Files which need to be ignored
var avoided = [  
    "node_modules"
];

// Add the tests to the Mocha instance
(addFiles = function (dir) {
    fs.readdirSync(dir).filter(function (file) {
        if (!~avoided.indexOf(file)) {
            if (fs.statSync(dir + '/' + file).isDirectory()) {
                addFiles(dir + '/' + file);
            }
            return file.substr(-3) === '.js';
        }
    }).forEach(function (file) {
        mocha.addFile(dir + '/' + file);
    });
})(path.join(process.cwd(), process.argv[2] || "."));

// Run the files in Mocha
mocha.run(function (failures) {  
    process.exit(failures);
});

Damit sieht jetzt die require Anweisung so aus:

var searchCats = rootRequire('2_use_cases/searchCats');  

und der Aufruf von Mocha sieht so aus:

node runner.js tests  

Viel besser oder?

]]>