Besoin de travailler avec Jasmine

Je veux d'abord dire que je suis nouveau dans RequireJS et encore plus récent chez Jasmine.

J'ai des problèmes avec SpecRunner et nécessite JS. J'ai suivi les tutoriels d'Uzi Kilon et Ben Nadel (avec d'autres) et ils ont aidé certains, mais j'ai encore des problèmes.

Il semble que, s'il y a une erreur qui est lancée dans le test (je peux penser à une en particulier, une erreur de type), le spec runner html s'affichera. Cela me dit que j'ai des problèmes dans le javascript. Cependant, après avoir corrigé cette erreur, aucun HTML n'est affiché. Je ne peux pas faire afficher le coureur de test. Quelqu'un peut-il trouver quelque chose de mal avec mon code qui pourrait causer ce problème?

Voici ma structure de répertoire :

Root |-> lib |-> jasmine |-> lib (contains all of the jasmine lib) |-> spec |-> src |-> jquery (jquery js file) |-> require (require js file) index.html (spec runner) specRunner.js 

Voici le code HTML de SpecRunner (index) :

 <!doctype html> <html lang="en"> <head> <title>Javascript Tests</title> <link rel="stylesheet" href="lib/jasmine/lib/jasmine.css"> <script src="lib/jasmine/lib/jasmine.js"></script> <script src="lib/jasmine/lib/jasmine-html.js"></script> <script src="lib/jquery/jquery.js"></script> <script data-main="specRunner" src="lib/require/require.js"></script> <script> require({ paths: { spec: "lib/jasmine/spec" } }, [ // Pull in all your modules containing unit tests here. "spec/notepadSpec" ], function () { jasmine.getEnv().addReporter(new jasmine.HtmlReporter()); jasmine.getEnv().execute(); }); </script> </head> <body> </body> </html> 

Voici le specRunner.js (config)

 require.config({ urlArgs: 'cb=' + Math.random(), paths: { jquery: 'lib/jquery', jasmine: 'lib/jasmine/lib/jasmine', 'jasmine-html': 'lib/jasmine/lib/jasmine-html', spec: 'lib/jasmine/spec/' }, shim: { jasmine: { exports: 'jasmine' }, 'jasmine-html': { deps: ['jasmine'], exports: 'jasmine' } } }); 

Voici une spécification:

 require(["../lib/jasmine/src/notepad"], function (notepad) { describe("returns titles", function() { expect(notepad.noteTitles()).toEqual(""); }); }); 

La source du bloc-notes:

 define(['lib/jasmine/src/note'], function (note) { var notes = [ new note('pick up the kids', 'dont forget to pick up the kids'), new note('get milk', 'we need two gallons of milk') ]; return { noteTitles: function () { var val; for (var i = 0, ii = notes.length; i < ii; i++) { //alert(notes[i].title); val += notes[i].title + ' '; } return val; } }; }); 

Et la source Note (JIC):

 define(function (){ var note = function(title, content) { this.title = title; this.content = content; }; return note; }); 

Je me suis assuré que, en ce qui concerne l'application, les chemins sont corrects. Une fois que je parviens à travailler, je peux jouer avec la configuration de ces chemins afin qu'il ne soit pas si plaisant.

J'ai réussi à travailler avec des essais et des erreurs. Le problème principal était que lorsque vous écrivez des spécifications, ce n'est pas nécessaire que vous souhaitez créer, vous souhaitez utiliser définir:

Original:

 require(["/lib/jasmine/src/notepad"], function (notepad) { describe("returns titles", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk"); }); }); 

Travail:

 define(["lib/jasmine/src/notepad"], function (notepad) { describe("returns titles", function () { it("something", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk "); }); }); }); 

Après avoir fait des recherches, il est apparu clairement que, lors de l'utilisation de RequireJS, tout ce que vous voulez que le require () use doit être enveloppé dans une définition (semble évident maintenant je suppose). Vous pouvez le voir, dans le fichier specRunner.js, un besoin est utilisé lors de l'exécution des tests (donc, il faut «définir» les spécifications.

L'autre problème est que, lors de la création de spécifications, la décrire () ET l'elle () sont nécessaires (pas seulement la description comme je l'ai fait dans l'exemple affiché).

Original:

 describe("returns titles", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk"); }); 

Travail:

 describe("returns titles", function () { it("something", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk "); }); }); 

J'ai également changé où existe le coureur de test, mais c'était un réfactor et n'a pas changé le résultat des tests.

Encore une fois, voici les fichiers et les modifications:

Note.js: est resté le même

Notepad.js: est resté le même

Index.html:

 <!doctype html> <html lang="en"> <head> <title>Javascript Tests</title> <link rel="stylesheet" href="lib/jasmine/lib/jasmine.css"> <script data-main="specRunner" src="lib/require/require.js"></script> </head> <body> </body> </html> 

SpecRunner.js:

 require.config({ urlArgs: 'cb=' + Math.random(), paths: { jquery: 'lib/jquery', 'jasmine': 'lib/jasmine/lib/jasmine', 'jasmine-html': 'lib/jasmine/lib/jasmine-html', spec: 'lib/jasmine/spec/' }, shim: { jasmine: { exports: 'jasmine' }, 'jasmine-html': { deps: ['jasmine'], exports: 'jasmine' } } }); require(['jquery', 'jasmine-html'], function ($, jasmine) { var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function (spec) { return htmlReporter.specFilter(spec); }; var specs = []; specs.push('lib/jasmine/spec/notepadSpec'); $(function () { require(specs, function (spec) { jasmineEnv.execute(); }); }); }); 

NotepadSpec.js:

 define(["lib/jasmine/src/notepad"], function (notepad) { describe("returns titles", function () { it("something", function() { expect(notepad.noteTitles()).toEqual("pick up the kids get milk"); }); }); }); 

Il suffit d'ajouter ceci comme une réponse alternative pour les personnes qui utilisent Jasmine 2.0 autonome. Je crois que cela peut également fonctionner pour Jasmine 1.3, mais la syntaxe asynchrone est différente et gentille.

Voici mon fichier SpecRunner.html modifié:

 <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Jasmine Spec Runner v2.0.0</title> <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.0/jasmine_favicon.png"> <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/jasmine.css"> <!-- Notice that I just load Jasmine normally --> <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script> <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script> <script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script> <!-- Here we load require.js but we do not use data-main. Instead we will load the the specs separately. In short we need to load the spec files synchronously for this to work. --> <script type="text/javascript" src="js/vendor/require.min.js"></script> <!-- I put my require js config inline for simplicity --> <script type="text/javascript"> require.config({ baseUrl: 'js', shim: { 'underscore': { exports: '_' }, 'react': { exports: 'React' } }, paths: { jquery: 'vendor/jquery.min', underscore: 'vendor/underscore.min', react: 'vendor/react.min' } }); </script> <!-- I put my spec files here --> <script type="text/javascript" src="spec/a-spec.js"></script> <script type="text/javascript" src="spec/some-other-spec.js"></script> </head> <body> </body> </html> 

Voici un exemple de fichier de spécifications:

 describe("Circular List Operation", function() { // The CircularList object needs to be loaded by RequireJs // before we can use it. var CircularList; // require.js loads scripts asynchronously, so we can use // Jasmine 2.0's async support. Basically it entails calling // the done function once require js finishes loading our asset. // // Here I put the require in the beforeEach function to make sure the // Circular list object is loaded each time. beforeEach(function(done) { require(['lib/util'], function(util) { CircularList = util.CircularList; done(); }); }); it("should know if list is empty", function() { var list = new CircularList(); expect(list.isEmpty()).toBe(true); }); // We can also use the async feature on the it function // to require assets for a specific test. it("should know if list is not empty", function(done) { require(['lib/entity'], function(entity) { var list = new CircularList([new entity.Cat()]); expect(list.isEmpty()).toBe(false); done(); }); }); }); 

Voici un lien de la section de support asynchrone des docs Jasmine 2.0: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support

Vous pouvez utiliser en combinaison avec des filtres avant pour tester les rappels asynchrones:

  beforeEach(function(done) { return require(['dist/sem-campaign'], function(campaign) { module = campaign; return done(); }); }); 

Une autre option pour Jasmine 2.0 autonome crée un fichier boot.js et le configure pour exécuter vos tests après que tous vos modules AMD ont été chargés.

Le cas idéal de l'utilisateur final pour les tests d'écriture dans notre cas était de ne pas énumérer tous nos fichiers ou dépendances de spécifications dans une seule liste explicite, et ont seulement l'obligation de déclarer vos fichiers * spec en tant que modules AMD avec des dépendances.

Exemple de spécifications idéales: spec / javascript / sampleController_spec.js

 require(['app/controllers/SampleController'], function(SampleController) { describe('SampleController', function() { it('should construct an instance of a SampleController', function() { expect(new SampleController() instanceof SampleController).toBeTruthy(); }); }); }); 

Idéalement, le comportement d'arrière-plan du chargement de la dépendance et de l'exécution des spécifications serait totalement opaque pour quiconque viendrait sur le projet souhaitant écrire des tests, et ils ne devront pas faire autre chose que créer un fichier * spec.js avec des dépendances AMD .

Pour que tout cela fonctionne, nous avons créé un fichier de démarrage et avons configuré Jasmine pour l'utiliser ( http://jasmine.github.io/2.0/boot.html ), et ajouté de la magie à l'enveloppe nécessite de temporiser temporairement les tests en cours jusqu'à la fin Nous avons nos déps chargés:

La section "Exécution" de notre boot.js :

 /** * ## Execution * * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. */ var currentWindowOnload = window.onload; // Stack of AMD spec definitions var specDefinitions = []; // Store a ref to the current require function window.oldRequire = require; // Shim in our Jasmine spec require helper, which will queue up all of the definitions to be loaded in later. require = function(deps, specCallback){ //push any module defined using require([deps], callback) onto the specDefinitions stack. specDefinitions.push({ 'deps' : deps, 'specCallback' : specCallback }); }; // window.onload = function() { // Restore original require functionality window.require = oldRequire; // Keep a ref to Jasmine context for when we execute later var context = this, requireCalls = 0, // counter of (successful) require callbacks specCount = specDefinitions.length; // # of AMD specs we're expecting to load // func to execute the AMD callbacks for our test specs once requireJS has finished loading our deps function execSpecDefinitions() { //exec the callback of our AMD defined test spec, passing in the returned modules. this.specCallback.apply(context, arguments); requireCalls++; // inc our counter for successful AMD callbacks. if(requireCalls === specCount){ //do the normal Jamsine HTML reporter initialization htmlReporter.initialize.call(context); //execute our Jasmine Env, now that all of our dependencies are loaded and our specs are defined. env.execute.call(context); } } var specDefinition; // iterate through all of our AMD specs and call require with our spec execution callback for (var i = specDefinitions.length - 1; i >= 0; i--) { require(specDefinitions[i].deps, execSpecDefinitions.bind(specDefinitions[i])); } //keep original onload in case we set one in the HTML if (currentWindowOnload) { currentWindowOnload(); } }; 

Nous gardons essentiellement nos spécifications de syntaxe AMD dans une pile, les diffusons, nécessitons les modules, exécutons le rappel avec nos assertions, puis exécutez Jasmine une fois que tout est terminé.

Cette configuration nous permet d'attendre que tous les modules AMD requis par nos tests individuels soient chargés et ne brisent pas les modèles AMD en créant des globaux. Il y a une petite horloge dans le fait que nous annulons temporairement l'exigence, et ne chargeons que notre code d'application en utilisant (notre `src_dir: dans jasmine.yml est vide), mais l'objectif global ici est de réduire les frais généraux d'écriture d'une spécification.

C'est ainsi que je fais pour exécuter une spécification jasmine dans un html utilisant AMD / requirejs pour toutes mes sources et spécifications.

Il s'agit de mon fichier index.html qui charge jasmine puis mon 'démarreur test d'unité':

 <html><head><title>unit test</title><head> <link rel="shortcut icon" type="image/png" href="/jasmine/lib/jasmine-2.1.3/jasmine_favicon.png"> <link rel="stylesheet" href="/jasmine/lib/jasmine-2.1.3/jasmine.css"> <script src="/jasmine/lib/jasmine-2.1.3/jasmine.js"></script> <script src="/jasmine/lib/jasmine-2.1.3/jasmine-html.js"></script> <script src="/jasmine/lib/jasmine-2.1.3/boot.js"></script> </head><body> <script data-main="javascript/UnitTestStarter.js" src="javascript/require.js"></script> </body></html> 

Et puis mon UnitTestStarter.js est quelque chose comme ceci:

 require.config({ "paths": { .... }); require(['MySpec.js'], function() { jasmine.getEnv().execute(); })