waitseconds node example javascript unit-testing requirejs bdd jasmine

javascript - node - requirejs npm



Obtener requirejs para trabajar con Jasmine (5)

Primero quiero decir que soy nuevo en RequireJS y aún más nuevo para Jasmine.

Estoy teniendo problemas con SpecRunner y necesito JS. He estado siguiendo los tutoriales de Uzi Kilon y Ben Nadel (junto con algunos otros) y han ayudado a algunos, pero todavía tengo algunos problemas.

Parece que, si se arroja un error en la prueba (puedo pensar en uno en particular, un error de tipo) se mostrará el corredor espectral html. Esto me dice que tengo algunos problemas en el javascript. Sin embargo, después de corregir esos errores ya no se muestra HTML. No puedo hacer que el corredor de prueba se muestre en absoluto. ¿Alguien puede encontrar algo mal con mi código que podría causar este problema?

Aquí está mi estructura de directorio :

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

Aquí está el HTML de SpecRunner (índice) :

<!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>

Aquí está el 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'' } } });

Aquí hay una especificación:

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

La fuente del bloc de notas:

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; } }; });

Y la fuente de la nota (JIC):

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

Me he asegurado de que, en lo que respecta a la aplicación, las rutas sean correctas. Una vez que funcione, puedo jugar a configurar esos caminos para que no sea tan asqueroso.


Así es como hago para ejecutar una especificación de jazmín en un html usando AMD / requirejs para todas mis fuentes y especificaciones.

Este es mi archivo index.html que carga jazmín y luego mi ''unit test starter'':

<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>

y luego mi UnitTestStarter.js es algo como esto:

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


Me las arreglé para que esto funcionara con algo de prueba y error. El problema principal era que cuando escribes especificaciones no es un requerimiento que quieras crear, quieres usar define:

Original:

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

Trabajando:

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

Después de investigar un poco, quedó claro que, al usar RequireJS, cualquier cosa que desee que requiera () debe estar envuelta en una definición (parece obvio ahora, supongo). Puede ver que, en el archivo specRunner.js, se utiliza un requerimiento al ejecutar las pruebas (por lo tanto, la necesidad de "definir" las especificaciones).

El otro problema es que, al crear las especificaciones, es necesario describir () Y el it (no solo describir como lo hice en el ejemplo publicado).

Original:

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

Trabajando:

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

También cambié alrededor de donde existe el corredor de prueba, pero este era un refactor y no cambió el resultado de las pruebas.

Nuevamente, aquí están los archivos y los cambios:

note.js: se mantuvo igual

notepad.js: se mantuvo igual

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"); }); }); });


Otra opción para Jasmine 2.0 standalone es crear un archivo boot.js y configurarlo para ejecutar sus pruebas después de que todos sus módulos AMD hayan sido cargados.

El caso ideal para el usuario final para escribir pruebas en nuestro caso era no tener que enumerar todos nuestros archivos o dependencias en una lista explícita, y solo tener el requisito de declarar sus archivos * spec como módulos AMD con dependencias.

Ejemplo de especificación ideal: 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(); }); }); });

Idealmente, el comportamiento de fondo de cargar la dependencia y ejecutar las especificaciones sería totalmente opaco para cualquiera que ingrese al proyecto que desee escribir pruebas, y no necesitarán hacer nada más que crear un archivo * spec.js con dependencias de AMD. .

Para que todo esto funcionara, creamos un archivo de inicio y configuramos Jasmine para usarlo ( http://jasmine.github.io/2.0/boot.html ), y añadimos algo de magia para envolver y exigir el retraso temporal de las pruebas hasta después tenemos nuestros deps cargados:

Nuestra sección de "Ejecución" 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(); } };

Básicamente mantenemos nuestras especificaciones de sintaxis de AMD en una pila, las sacamos, requerimos los módulos, ejecutamos la devolución de llamada con nuestras aserciones, y luego ejecutamos Jasmine una vez que todo termina de cargarse.

Esta configuración nos permite esperar hasta que se carguen todos los módulos de AMD requeridos por nuestras pruebas individuales, y no rompe los patrones de AMD creando globales. Hay un poco de hackeo en el hecho de que `src_dir: temporalmente require, y solo `src_dir: nuestro código de aplicación utilizando require (nuestro `src_dir: en jasmine.yml está vacío), pero el objetivo general aquí es reducir la sobrecarga de escribir una especificación.


Solo agregue esto como una respuesta alternativa para las personas que están utilizando Jasmine 2.0 independiente. Creo que esto también puede funcionar para Jasmine 1.3, pero la sintaxis asíncrona es diferente y fea.

Aquí está mi archivo SpecRunner.html modificado:

<!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>

Ahora aquí hay un archivo de especificación de ejemplo:

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(); }); }); });

Aquí hay un enlace de la sección de soporte asincrónico de los documentos de Jasmine 2.0: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support


puede usar done in combo with before filters para probar devoluciones de llamada asincrónicas:

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