Pourquoi est-ce que je vois « define non défini » lors de l'exécution d'un test Mocha avec RequireJS?
-
25-10-2019 - |
Question
J'essaie de comprendre comment développer le code Javascript autonome. Je veux écrire un code javscript avec des tests et des modules, depuis la ligne de commande. J'ai donc installé node.js
et npm
ainsi que les bibliothèques requirejs
, underscore
et mocha
.
Ma structure annuaire se présente comme suit:
> tree .
.
├── node_modules
├── src
│ └── utils.js
└── test
└── utils.js
où src/utils.js
est un petit module qui je suis en train d'écrire, 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)
...
Alors mes questions sont:
- Est-ce la bonne façon de code de structure?
- Pourquoi mon test ne fonctionne pas?
- Quelle est la meilleure façon d'apprendre ce genre de chose? J'ai du mal à trouver de bons exemples avec Google.
Merci ...
[désolé - momentanément affiché des résultats de code erroné; fixe maintenant]
PS J'utilise requirejs parce que je veux aussi exécuter ce code (ou une partie de celui-ci) à partir d'un navigateur, plus tard.
Mise à jour / Solution
Quelque chose qui est 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 solution
La raison pour laquelle votre test ne fonctionne pas 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 les CommonJS exigent standard, vous devez ajouter un peu de boilerplate en haut de votre fichier src/utils.js
si la fonction define
de RequireJS est chargé.
Cependant, depuis RequireJS a été conçu pour être en mesure d'exiger le code source orienté navigateur web « classique », j'ai tendance à utiliser le modèle suivant avec mes bibliothèques Node.js que je veux aussi en cours d'exécution dans le navigateur:
if(typeof require != 'undefined') {
// Require server-side-specific modules
}
// Insert code here
if(typeof module != 'undefined') {
module.exports = whateverImExporting;
}
Ceci a l'avantage de ne pas nécessiter une bibliothèque supplémentaire pour les autres utilisateurs Node.js et fonctionne généralement bien avec RequireJS sur le client.
Une fois que vous obtenez votre code en cours d'exécution dans Node.js, vous pouvez commencer à tester. Personnellement, je préfère encore plus Expresso mocha, même si son cadre de test successeur.
Autres conseils
La documentation Mocha manque sur la façon de mettre ce genre de 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 des fichiers de navigateur en utilisant require.js au travail en Mocha sous le nœud: Mocha a pour avoir les fichiers ajoutés à ses suites avec addFile
:
mocha.addFile('lib/tests/Main_spec_node');
Et deuxièmement, l'utilisation beforeEach
avec le rappel en option 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);
});
});
});
Je créé un bootstrap pour savoir comment obtenir ce travail tout: https://github.com/clubajax/mocha-bootstrap
Vous essayez d'exécuter des modules JS conçus pour les navigateurs (AMD), mais dans le back-end, il pourrait ne pas fonctionner (comme les modules sont chargés le chemin CommonJS). En raison de cela, vous ferez face à deux problèmes:
- Define ne sont pas définis
- 0 exécuter des tests
Dans le browserdefine
sera défini. Il sera réglé lorsque vous avez besoin quelque chose avec requirejs. Mais NodeJS charge les modules comme CommonJS. define
dans ce cas n'est pas défini. Mais il sera défini lorsque nous avons besoin avec requirejs!
Cela signifie que maintenant nous le code NÉCESSITANT de manière asynchrone, et il apporte 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 je devais requirejs configure (amd) pour charger les modules, nous n'utilisons pas besoin (nœud / 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 voulez 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 réponse de David n'a pas été assez clair, je juste besoin d'ajouter ceci:
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
Vers le haut du fichier js où j'utilise define
, comme décrit dans RequireJS docs ( » construire des modules de noeuds avec AMD ou RequireJS ") et dans le même dossier ajouter le package amdefine
:
npm install amdefine
Cela crée le dossier node_modules
avec le module amdefine
à l'intérieur.
Je ne me requirejs
donc je ne suis pas sûr de ce qui ressemble de syntaxe aiment, mais ce que je fais pour exécuter du code à la fois dans les node
et browser
:
Pour les importations, déterminer si nous courons dans le nœud ou le navigateur:
var root = typeof exports !== "undefined" && exports !== null ? exports : window;
Ensuite, nous pouvons saisissons toutes les dépendances correctement (ils seront soit disponibles déjà si dans le navigateur ou que nous utilisons require
):
var foo = root.foo;
if (!foo && (typeof require !== 'undefined')) {
foo = require('./foo');
}
var Bar = function() {
// do something with foo
}
Et puis toutes les fonctionnalités qui doivent être utilisés par d'autres fichiers, nous exportons à la racine:
root.bar = Bar;
En ce qui concerne les exemples, GitHub
est une grande source. Il suffit d'aller et de vérifier le code de votre bibliothèque préférée pour voir comment ils l'ont fait :) je mocha
pour tester une bibliothèque javascript qui peut être utilisé aussi bien dans le navigateur et le noeud. Le code est disponible à https://github.com/bunkat/later .