J'essaie de comprendre comment développer du code Javascript autonome. Je veux écrire du code Javscript avec des tests et des modules, à partir de la ligne de commande. J'ai donc installé node.js
et npm
ainsi que les bibliothèques requirejs
, underscore
et mocha
.
Ma structure de répertoire ressemble à ceci:
> tree .
.
├── node_modules
├── src
│ └── utils.js
└── test
└── utils.js
où src/utils.js
est un petit module que j'écris, avec le code suivant:
> cat src/utils.js
define(['underscore'], function () {
"use strict";
if ('function' !== typeof Object.beget) {
Object.beget = function (o) {
var f = function () {
};
f.prototype = o;
return new f();
};
}
});
et test/utils.js
est le test:
> cat test/utils.js
var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});
requirejs(['../src/utils'], function(utils) {
suite('utils', function() {
test('should always work', function() {
assert.equal(1, 1);
})
})
});
que j'essaie ensuite d'exécuter à partir du répertoire de niveau supérieur (donc mocha
voit le répertoire test
):
> mocha
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Calling node's require("../src/utils") failed with error: ReferenceError: define is not defined
at /.../node_modules/requirejs/bin/r.js:2276:27
at Function.execCb (/.../node_modules/requirejs/bin/r.js:1872:25)
at execManager (/.../node_modules/requirejs/bin/r.js:541:31)
...
Mes questions sont donc:
Merci...
[désolé - résultats momentanément affichés à partir d'un mauvais code; réparé maintenant]
PS J'utilise requirejs parce que je veux aussi exécuter ce code (ou une partie) à partir d'un navigateur, plus tard.
Mise à jour/Solution
Quelque chose qui ne figure pas dans les réponses ci-dessous est que je devais utiliser mocha -u tdd
pour le style de test ci-dessus. Voici le test final (qui nécessite également assert) et son utilisation:
> cat test/utils.js
var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});
requirejs(['../src/utils', 'assert'], function(utils, assert) {
suite('utils', function() {
test('should always work', function() {
assert.equal(1, 1);
})
})
});
> mocha -u tdd
.
✔ 1 tests complete (1ms)
La raison pour laquelle votre test ne s'exécute pas est parce que src/utils.js
n'est pas une bibliothèque Node.js valide.
Selon la documentation RequireJS, afin de coexister avec Node.js et CommonJS nécessitent la norme, vous devez ajouter un peu de passe-partout en haut de votre src/utils.js
file afin que la fonction define
de RequireJS soit chargée.
Cependant, étant donné que RequireJS a été conçu pour pouvoir exiger du code source orienté navigateur Web "classique", j'ai tendance à utiliser le modèle suivant avec mes bibliothèques Node.js que je souhaite également exécuter dans le navigateur:
if(typeof require != 'undefined') {
// Require server-side-specific modules
}
// Insert code here
if(typeof module != 'undefined') {
module.exports = whateverImExporting;
}
Cela a l'avantage de ne pas nécessiter de bibliothèque supplémentaire pour les autres utilisateurs de Node.js et fonctionne généralement bien avec RequireJS sur le client.
Une fois votre code exécuté dans Node.js, vous pouvez commencer les tests. Personnellement, je préfère toujours expresso à moka, même si c'est le cadre de test successeur.
La documentation Mocha manque sur la façon de configurer ces choses, et il est perplexe de comprendre à cause de tous les tours de magie qu'il fait sous le capot.
J'ai trouvé les clés pour obtenir les fichiers du navigateur en utilisant require.js pour travailler dans Mocha sous Node: Mocha has pour que les fichiers soient ajoutés à ses suites avec addFile
:
mocha.addFile('lib/tests/Main_spec_node');
Et deuxièmement, utilisez beforeEach
avec le rappel facultatif pour charger vos modules de manière asynchrone:
describe('Testing "Other"', function(done){
var Other;
beforeEach(function(done){
requirejs(['lib/Other'], function(_File){
Other = _File;
done(); // #1 Other Suite will run after this is called
});
});
describe('#1 Other Suite:', function(){
it('Other.test', function(){
chai.expect(Other.test).to.equal(true);
});
});
});
J'ai créé un bootstrap pour savoir comment tout cela fonctionne: https://github.com/clubajax/mocha-bootstrap
Vous essayez d'exécuter des modules JS conçus pour les navigateurs (AMD), mais dans le backend cela peut ne pas fonctionner (car les modules sont chargés de la manière commonjs). Pour cette raison, vous serez confronté à deux problèmes:
Dans le navigateur, define
sera défini. Il sera défini lorsque vous demanderez quelque chose avec requirejs. Mais nodejs charge les modules de la manière commune. define
dans ce cas n'est pas défini. Mais il sera défini lorsque nous aurons besoin de requirejs!
Cela signifie que maintenant nous demandons du code de manière asynchrone, et cela pose le deuxième problème, un problème avec l'exécution asynchrone. https://github.com/mochajs/mocha/issues/362
Voici un exemple de travail complet. Regardez que j'ai dû configurer requirejs (AMD) pour charger les modules, nous n'utilisons pas require (node / commonjs) pour charger nos modules.
> cat $PROJECT_HOME/test/test.js
var requirejs = require('requirejs');
var path = require('path')
var project_directory = path.resolve(__dirname, '..')
requirejs.config({
nodeRequire: require,
paths: {
'widget': project_directory + '/src/js/some/widget'
}
});
describe("Mocha needs one test in order to wait on requirejs tests", function() {
it('should wait for other tests', function(){
require('assert').ok(true);
});
});
requirejs(['widget/viewModel', 'assert'], function(model, assert){
describe('MyViewModel', function() {
it("should be 4 when 2", function () {
assert.equal(model.square(2),4)
})
});
})
Et pour le module que vous souhaitez tester:
> cat $PROJECT_HOME/src/js/some/widget/viewModel.js
define(["knockout"], function (ko) {
function VideModel() {
var self = this;
self.square = function(n){
return n*n;
}
}
return new VideModel();
})
Juste au cas où réponse de David n'était pas assez clair, j'avais juste besoin d'ajouter ceci:
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
En haut du fichier js où j'utilise define
, comme décrit dans les documents RequireJS ( "Création de modules de noeud avec AMD ou RequireJS" ) et dans le même dossier, ajoutez le amdefine
package:
npm install amdefine
Cela crée le node_modules
dossier avec le module amdefine
à l'intérieur.