node.js - nodejs - node test before
¿Cómo acceder y probar una función interna(no exportadora) en un módulo node.js? (6)
EDITAR:
Cargar un módulo usando vm
puede causar un comportamiento inesperado (por instanceof
operador instanceof
ya no trabaja con objetos que se crean en dicho módulo porque los prototipos globales son diferentes de los utilizados en el módulo cargado normalmente con require
). Ya no uso la técnica a continuación y en su lugar uso el módulo de rewire . Funciona maravillosamente. Aquí está mi respuesta original:
Elaborando sobre la respuesta de srosh ...
Se siente un poco hacky, pero escribí un simple módulo "test_utils.js" que debería permitirle hacer lo que quiera sin tener exportaciones condicionales en los módulos de su aplicación:
var Script = require(''vm'').Script,
fs = require(''fs''),
path = require(''path''),
mod = require(''module'');
exports.expose = function(filePath) {
filePath = path.resolve(__dirname, filePath);
var src = fs.readFileSync(filePath, ''utf8'');
var context = {
parent: module.parent, paths: module.paths,
console: console, exports: {}};
context.module = context;
context.require = function (file){
return mod.prototype.require.call(context, file);};
(new Script(src)).runInNewContext(context);
return context;};
Hay algunas cosas más que se incluyen en el objeto de module
gobal de un módulo de nodo que también podría necesitar entrar en el objeto de context
anterior, pero este es el conjunto mínimo que necesito para que funcione.
Aquí hay un ejemplo con BDD mocha:
var util = require(''./test_utils.js''),
assert = require(''assert'');
var appModule = util.expose(''/path/to/module/modName.js'');
describe(''appModule'', function(){
it(''should test notExposed'', function(){
assert.equal(6, appModule.notExported(3));
});
});
Estoy tratando de averiguar cómo probar las funciones internas (es decir, no exportadas) en nodejs (preferiblemente con mocha o jazmín). ¡Y no tengo ni idea!
Digamos que tengo un módulo como ese:
function exported(i) {
return notExported(i) + 1;
}
function notExported(i) {
return i*2;
}
exports.exported = exported;
Y la siguiente prueba (mocha):
var assert = require(''assert''),
test = require(''../modules/core/test'');
describe(''test'', function(){
describe(''#exported(i)'', function(){
it(''should return (i*2)+1 for any given i'', function(){
assert.equal(3, test.exported(1));
assert.equal(5, test.exported(2));
});
});
});
¿Hay alguna forma de probar la función no exportada sin exportarla, ya que no está destinada a ser expuesta?
El módulo de rewire es definitivamente la respuesta.
Aquí está mi código para acceder a una función no exportada y probarla usando Mocha.
application.js:
function logMongoError(){
console.error(''MongoDB Connection Error. Please make sure that MongoDB is running.'');
}
test.js:
var rewire = require(''rewire'');
var chai = require(''chai'');
var should = chai.should();
var app = rewire(''../application/application.js'');
logError = app.__get__(''logMongoError'');
describe(''Application module'', function() {
it(''should output the correct error'', function(done) {
logError().should.equal(''MongoDB Connection Error. Please make sure that MongoDB is running.'');
done();
});
});
El truco es establecer la variable de entorno NODE_ENV
a algo así como test
y luego exportarlo condicionalmente.
Suponiendo que no haya instalado Mocha de forma global, podría tener un Makefile en la raíz del directorio de su aplicación que contenga lo siguiente:
REPORTER = dot
test:
@NODE_ENV=test ./node_modules/.bin/mocha /
--recursive --reporter $(REPORTER) --ui bbd
.PHONY: test
Este archivo make establece NODE_ENV antes de ejecutar mocha. Luego puede ejecutar sus pruebas de mocha con make test
en la línea de comando.
Ahora, puede exportar de manera condicional su función que generalmente no se exporta solo cuando se ejecutan sus pruebas de mocha:
function exported(i) {
return notExported(i) + 1;
}
function notExported(i) {
return i*2;
}
if (process.env.NODE_ENV === "test") {
exports.notExported = notExported;
}
exports.exported = exported;
La otra respuesta sugirió usar un módulo vm para evaluar el archivo, pero esto no funciona y arroja un error que indica que las exportaciones no están definidas.
He encontrado una manera bastante simple que le permite probar, espiar y burlarse de esas funciones internas desde las pruebas:
Digamos que tenemos un módulo de nodo como este:
mymodule.js:
------------
"use strict";
function myInternalFn() {
}
function myExportableFn() {
myInternalFn();
}
exports.myExportableFn = myExportableFn;
Si ahora queremos probar y espiar y myInternalFn
sin exportarlo en producción , tenemos que mejorar el archivo de esta manera:
my_modified_module.js:
----------------------
"use strict";
var testable; // <-- this is new
function myInternalFn() {
}
function myExportableFn() {
testable.myInternalFn(); // <-- this has changed
}
exports.myExportableFn = myExportableFn;
// the following part is new
if( typeof jasmine !== "undefined" ) {
testable = exports;
} else {
testable = {};
}
testable.myInternalFn = myInternalFn;
Ahora puede probar, espiar y burlarse de myInternalFn
cualquier lugar donde lo use como testable.myInternalFn
y en producción no se exporta .
Trabajando con Jasmine, traté de ir más profundo con la solución propuesta por Anthony Mayfield , basada en rewire .
Implementé la siguiente función ( Precaución : aún no se ha probado completamente, solo se compartió como una estrategia posible) :
function spyOnRewired() {
const SPY_OBJECT = "rewired"; // choose preferred name for holder object
var wiredModule = arguments[0];
var mockField = arguments[1];
wiredModule[SPY_OBJECT] = wiredModule[SPY_OBJECT] || {};
if (wiredModule[SPY_OBJECT][mockField]) // if it was already spied on...
// ...reset to the value reverted by jasmine
wiredModule.__set__(mockField, wiredModule[SPY_OBJECT][mockField]);
else
wiredModule[SPY_OBJECT][mockField] = wiredModule.__get__(mockField);
if (arguments.length == 2) { // top level function
var returnedSpy = spyOn(wiredModule[SPY_OBJECT], mockField);
wiredModule.__set__(mockField, wiredModule[SPY_OBJECT][mockField]);
return returnedSpy;
} else if (arguments.length == 3) { // method
var wiredMethod = arguments[2];
return spyOn(wiredModule[SPY_OBJECT][mockField], wiredMethod);
}
}
Con una función como esta, puede espiar los métodos de objetos no exportados y las funciones de nivel superior no exportadas, de la siguiente manera:
var dbLoader = require("rewire")("../lib/db-loader");
// Example: rewired module dbLoader
// It has non-exported, top level object ''fs'' and function ''message''
spyOnRewired(dbLoader, "fs", "readFileSync").and.returnValue(FULL_POST_TEXT); // method
spyOnRewired(dbLoader, "message"); // top level function
Entonces puedes establecer expectativas como estas:
expect(dbLoader.rewired.fs.readFileSync).toHaveBeenCalled();
expect(dbLoader.rewired.message).toHaveBeenCalledWith(POST_DESCRIPTION);
puede crear un nuevo contexto utilizando el módulo vm y evaluar el archivo js en él, algo así como repl hace. entonces tienes acceso a todo lo que declara.