fstab
estoy usando Moca para realizar pruebas unitarias de una aplicación escrita para Node.js.
Me pregunto si es posible realizar pruebas unitarias de funciones que no se han exportado en un módulo.
Ejemplo:
Tengo muchas funciones definidas así en foobar.js
:
function private_foobar1(){
...
}
function private_foobar2(){
...
}
Y algunas funciones exportadas como públicas:
exports.public_foobar3 = function(){
...
}
El caso de prueba está estructurado de la siguiente manera:
describe("private_foobar1", function() {
it("should do stuff", function(done) {
var stuff = foobar.private_foobar1(filter);
should(stuff).be.ok;
should(stuff).....
Obviamente esto no funciona, ya que private_foobar1
no se exporta.
¿Cuál es la forma correcta de realizar pruebas unitarias de métodos privados? ¿Mocha tiene algunos métodos incorporados para hacer eso?
Revisar la renovar el alambrado de módulo. Le permite obtener (y manipular) variables y funciones privadas dentro de un módulo.
Entonces, en su caso, el uso sería algo como:
var rewire = require('rewire'),
foobar = rewire('./foobar'); // Bring your module in with rewire
describe("private_foobar1", function() {
// Use the special '__get__' accessor to get your private function.
var private_foobar1 = foobar.__get__('private_foobar1');
it("should do stuff", function(done) {
var stuff = private_foobar1(filter);
should(stuff).be.ok;
should(stuff).....
-
@Jaro La mayor parte de mi código está en forma de módulos AMD, que no puede volver a cablear resolver (porque los módulos AMD son funciones, pero el recableado no puede manejar “variables dentro de las funciones”). O se transpila, otro escenario que rewire no puede manejar. En realidad, las personas que van a ver la reconexión harían bien en leer primero las limitaciones (vinculadas anteriormente) antes de intentar usarla. No tengo una sola aplicación que a) necesite exportar cosas “privadas” yb) no tenga una limitación de recableado.
– Luis
5 de abril de 2017 a las 10:25
-
Solo un pequeño punto, la cobertura de código puede fallar al detectar pruebas escritas como esta. Al menos eso es lo que he visto usando la herramienta de cobertura integrada de Jest.
– Mike Stead
17 de abril de 2017 a las 8:36
-
Rewire tampoco funciona bien con la herramienta de simulación automática de jest. Todavía estoy buscando una forma de aprovechar los beneficios de jest y acceder a algunas variables privadas.
– btburton42
27/09/2017 a las 17:16
-
Así que traté de hacer que esto funcionara, pero estoy usando mecanografiado, lo que supongo que está causando este problema. Básicamente me sale el siguiente error:
Cannot find module '../../package' from 'node.js'
. ¿Alguien familiarizado con esto?– sacudir
28 de noviembre de 2017 a las 7:45
-
rewire está funcionando bien en
.ts
,typescript
corro usandots-node
@clu– muthukumar selvaraj
19 de enero de 2018 a las 9:07
Luis
Si el módulo no exporta la función, el código de prueba no puede llamarla fuera del módulo. Eso se debe a cómo funciona JavaScript, y Mocha por sí solo no puede eludir esto.
En los pocos casos en los que determiné que probar una función privada es lo correcto, configuré alguna variable de entorno que mi módulo verifica para determinar si se está ejecutando en una configuración de prueba o no. Si se ejecuta en la configuración de prueba, exporta funciones adicionales a las que luego puedo llamar durante la prueba.
La palabra “medio ambiente” se usa vagamente aquí. Podría significar comprobar process.env
o algo más que pueda comunicar al módulo “ahora lo están probando”. Los casos en los que tuve que hacer esto fueron en un RequerirJS ambiente, y he usado module.config
para este propósito.
-
La exportación condicional de valores no parece ser compatible con los módulos ES6. Me estoy poniendo
SyntaxError: 'import' and 'export' may only appear at the top level
– aij
29 de febrero de 2016 a las 16:46
-
@aij sí debido a las exportaciones estáticas de ES6 que no puede usar
import
,export
dentro de un bloque. Eventualmente, podrá lograr este tipo de cosas en ES6 con el cargador del sistema. Una forma de evitarlo ahora es usarmodule.exports = process.env.NODE_ENV === 'production' ? require('prod.js') : require('dev.js')
y almacene sus diferencias de código es6 en esos archivos respectivos.– cchamberlain
1 de junio de 2016 a las 1:51
-
Supongo que si tiene una cobertura completa, entonces está probando todas sus funciones privadas, ya sea que las haya expuesto o no.
– Ziggy
11 de agosto de 2017 a las 23:37
-
@aij Puede exportar condicionalmente… vea esta respuesta: stackoverflow.com/questions/39583958/…
– Ray Loveless
14 de agosto de 2019 a las 21:36
Rémi Becheras
Aquí hay un muy buen flujo de trabajo para probar sus métodos privados explica Philip Walton, un ingeniero de Google en su blog.
Principio
- Escribe tu código normalmente
- Vincule sus métodos privados al objeto en un bloque de código separado y márquelo con un
_
(por ejemplo) - Rodea ese bloque de código con comentarios de inicio y finalización
Luego use una tarea de compilación o su propio sistema de compilación (por ejemplo grunt-strip-code) para eliminar este bloque para compilaciones de producción.
Tus compilaciones de prueba tienen acceso a tu API privada y tus compilaciones de producción no.
Retazo
Escribe tu código así:
var myModule = (function() {
function foo() {
// Private function `foo` inside closure
return "foo"
}
var api = {
bar: function() {
// Public function `bar` returned from closure
return "bar"
}
}
/* test-code */
api._foo = foo
/* end-test-code */
return api
}())
Y tu Gruñido tareas como esta:
grunt.registerTask("test", [
"concat",
"jshint",
"jasmine"
])
grunt.registerTask("deploy", [
"concat",
"strip-code",
"jshint",
"uglify"
])
Más adentro
En un artículo posteriorexplica el “por qué” de “probar métodos privados”
-
También encontré un complemento de webkit que parece que puede admitir un flujo de trabajo similar: webpack-strip-block
– JRulle
3 oct 2017 a las 11:10
famosogarkin
Si prefiere mantenerlo simple, simplemente exporte los miembros privados también, pero claramente separados de la API pública con alguna convención, por ejemplo, prefijelos con un _
o anidarlos bajo un solo privado objeto.
var privateWorker = function() {
return 1
}
var doSomething = function() {
return privateWorker()
}
module.exports = {
doSomething: doSomething,
_privateWorker: privateWorker
}
He añadido una función extra que nombro Interno() y devolver todas las funciones privadas desde allí. Este Interno() Luego se exporta la función. Ejemplo:
function Internal () {
return { Private_Function1, Private_Function2, Private_Function2}
}
// Exports --------------------------
module.exports = { PublicFunction1, PublicFunction2, Internal }
Puede llamar a las funciones internas de esta manera:
let test = require('.....')
test.Internal().Private_Function1()
Me gusta más esta solución porque:
- solo una funcion Interno() siempre se exporta. Este Interno() La función siempre se usa para probar funciones privadas.
- Es simple de implementar
- Bajo impacto en el código de producción (solo una función extra)
Pedro Mortensen
Hice un paquete npm para este propósito que podría resultarle útil: requerir-de
Básicamente, expone métodos no públicos al:
module.testExports = {
private_foobar1: private_foobar1,
private_foobar2: private_foobar2,
...
}
Nota: testExports
puede ser cualquier nombre válido que desee, excepto exports
por supuesto.
Y de otro módulo:
var requireFrom = require('require-from');
var private_foobar1 = requireFrom('testExports', './path-to-module').private_foobar1;
Pedro Mortensen
Sé que esta no es necesariamente la respuesta que está buscando, pero he descubierto que la mayoría de las veces, si vale la pena probar una función privada, vale la pena estar en su propio archivo.
Por ejemplo, en lugar de tener métodos privados en el mismo archivo que los públicos, así…
src/cosa/PublicInterface.js
function helper1 (x) {
return 2 * x;
}
function helper2 (x) {
return 3 * x;
}
export function publicMethod1(x) {
return helper1(x);
}
export function publicMethod2(x) {
return helper1(x) + helper2(x);
}
… lo divides así:
src/cosa/PublicInterface.js
import {helper1} from './internal/helper1.js';
import {helper2} from './internal/helper2.js';
export function publicMethod1(x) {
return helper1(x);
}
export function publicMethod2(x) {
return helper1(x) + helper2(x);
}
src/cosa/interno/helper1.js
export function helper1 (x) {
return 2 * x;
}
src/cosa/interno/helper2.js
export function helper2 (x) {
return 3 * x;
}
De esa manera, puede probar fácilmente helper1
y helper2
tal como está, sin usar Rewire y otra “magia” (que, según descubrí, tiene sus propios puntos débiles durante la depuración, o cuando intenta avanzar hacia TypeScript, sin mencionar la comprensión más pobre para los nuevos colegas). Y ellos estando en una subcarpeta llamada internal
o algo así, ayudará a evitar el uso accidental de ellos en lugares no previstos.
PD: Otro problema común con los métodos “privados” es que si desea probar publicMethod1
y publicMethod2
y burlarse de los ayudantes, nuevamente, normalmente necesita algo como Rewire para hacer eso. Sin embargo, si están en archivos separados, puede usar apoderado para hacerlo, que, a diferencia de Rewire, no necesita ningún cambio en su proceso de compilación, es fácil de leer y depurar, y funciona bien incluso con TypeScript.
Relacionado: stackoverflow.com/questions/14874208
– dskrvk
18/04/2017 a las 20:41