Sinon: Sandbox arroja el error 'no se puede eliminar una propiedad propia inexistente'

Creado en 18 ago. 2017  ·  14Comentarios  ·  Fuente: sinonjs/sinon

Intenté actualizar de 2.4.1 a 3.2.1 y encontré el siguiente problema. Este código funciona en 2.4.1:

        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });

Pero en 3.2.1 arroja una excepción: TypeError: Cannot stub non-existent own property google

No se menciona en la guía de migración, por lo que parece ser una regresión.

Bug Regression

Comentario más útil

~ Gracias por restaurar este comportamiento. ~

Quería agregar un caso de uso que admita stubbing de propiedades inexistentes.

En mi caso de uso, estoy almacenando una propiedad en un objeto de configuración. El objeto de configuración tiene varias claves opcionales y se inicializa cargando un archivo desde la máquina del desarrollador. Cuando ejecuto una prueba en particular, necesito que una de esas claves se establezca en un valor conocido y luego quiero restaurar el objeto del desarrollador como estaba.

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue) es una forma muy clara de transmitir esto. Es bueno que obtengo el mismo comportamiento, aunque no sé en tiempo de ejecución si la clave está configurada o no.

Todos 14 comentarios

Relacionado con # 1512.

Si la propiedad no existe, no es necesario que la agregue a la zona de pruebas. Simplemente sobrescríbalo. Pero sí, si funcionó antes y no hemos dicho explícitamente que debería cambiar, entonces es una regresión.

No estoy seguro de lo que deberíamos hacer aquí. ¿Actualizar los documentos para decir que la copia de seguridad de valores no existentes no tiene sentido y no es compatible, o lo hace posible?

Si la propiedad no existe, no es necesario que la agregue a la zona de pruebas. Simplemente sobrescríbalo.

Lo bueno de agregar la propiedad a la caja de arena es que sinon me ayuda a mantener limpio mi entorno de prueba global entre cada prueba a través de sandbox.restore() . Es una característica extremadamente útil, especialmente cuando se trata de bibliotecas de terceros como Google Maps, donde no controlo la API. Sería genial si pudiera funcionar en la línea 3.x.

También me di cuenta de que cometí el pecado de no dar un ejemplo completo. Mi caja de arena se está creando con el formato 2.4.1:

let sandbox;

before(() => { sandbox = sinon.sandbox.create(); })
afterEach(() => { sandbox.restore(); })

No estoy seguro de si eso es importante; disculpas por no proporcionarlo antes.

Creo que en escenarios como el que describe @ZebraFlesh , preferiría que el elemento de texto sea más explícito.

// not so explicit, doesn't work with [email protected]
beforeEach(function() {
    const spy = sandbox.spy();
    sandbox.stub(window, 'google').value({
        maps: {
            LatLng: x => x,
            Map: spy
        }
    }); 
});
// more explicit, works with sinon<strong i="9">@2</strong>, sinon<strong i="10">@3</strong>
function setGoogleMapsFixture(sandbox) {
    window.google = {
        maps: {
            LatLng: x => x,
            Map: sandbox.spy()
        }
    };
}

function removeGoogleMapsFixture() {
    delete window.google;
}

beforeEach(function() {
    setGoogleMapsFixture(sandbox)
});

// not using afterEach, as this only needs to happen
// after the last test in this block is run
after(function() {
    removeGoogleMapsFixture();
});

Con una configuración más explícita del accesorio como se describe anteriormente, no necesita una función en Sinon que permita la eliminación de propiedades propias no existentes.

No estoy seguro de lo que deberíamos hacer aquí. ¿Actualizar los documentos para decir que la copia de seguridad de valores no existentes no tiene sentido y no es compatible, o lo hace posible?

Si bien reconozco que podría ser conveniente en algunos escenarios (como el descrito por @ZebraFlesh), creo que es probable que la eliminación de propiedades propias no existentes conduzca a errores en las pruebas, donde la prueba pasa porque el autor escribió mal el nombre de la propiedad existente que pretendían tapar. Debemos apuntar a eliminar la posibilidad de errores donde podamos sin ser demasiado restrictivos.

Creo que la copia de seguridad de propiedades propias no existentes debería permanecer sin soporte. Deberíamos actualizar la documentación.

@mroderick Estoy de acuerdo con usted en que podría admitimos para stubs normales. Si dejamos de admitir este comportamiento, debemos eliminarlo allí también, para ser coherentes. Sería extraño admitir esta función solo fuera de las cajas de arena, ya que las cajas de arena suelen _agregar_ algunas posibilidades. Y quitar el soporte es una característica que rompe, por lo que también se requeriría un golpe importante.

Así que tampoco:

  • quite la marca de inmediato para las cajas de arena para corregir esta característica de ruptura

o y(?)

  • eliminar la funcionalidad de los stubs normales y de espacio aislado

    • lanzar una nueva versión principal con documentos actualizados

Con una configuración más explícita del accesorio como se describe anteriormente, no necesita una función en Sinon que permita la eliminación de propiedades propias no existentes.

Creo que funciona bien si su dispositivo nunca varía entre sus pruebas. Sin embargo, mi dispositivo lo hace. Un ejemplo simple cubre los casos de éxito y fracaso:

it('handles the success case', () => {
        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });
        // ... test, including asserting that the spy was called
});

it('handles the failure case', () => {
        const msg = 'test error';
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: sandbox.stub().throws(new Error(msg))
            }
        });
        // ... test, ignoring spy calls and instead focusing on error handling
});

El comportamiento en 2.x tiene la ventaja de que todo se limpia correctamente después de cada prueba a través de sandbox.restore() . Utilizando el ejemplo más explícito de configuración de dispositivos descrito anteriormente, supongo que podría eliminar la propiedad que no es propia en un gancho afterEach para lograr el mismo efecto.

Para resolver el problema de introducir errores potenciales al escribir inadvertidamente el nombre de una propiedad propia existente, sinon podría modificar la API pública:

  • stub.ownValue() : stubs solo poseen propiedades, arroja propiedades que no son propias
  • stub.value() : stubs solo propiedades que no son propias, arroja para propiedades propias

La API se vuelve más explícita y el consumidor se ve obligado a elegir la herramienta adecuada para la tarea en cuestión.

Esto está muy relacionado con la discusión en # 1508 (aunque se trata de stubs normales) h, donde @lucasfcosta tiene la vista opuesta, que _no deberíamos lanzar_ para las propiedades undefined . Sea lo que sea en lo que aterricemos, creo firmemente que debemos ser consistentes en las API de stubbing para stubs y sandboxes normales. No debemos apoyarlo en un caso y no en el otro.

Ahora mismo, la situación es:

  • stubs normales solían lanzar en 1.x, pero esto cambió en 2.0 y ya no se lanza
  • los sandboxes no solían lanzar, pero comenzaron a lanzar 3.1 (?)

Así que por un tiempo tuvimos paridad de funciones, pero luego la perdimos de nuevo ... No creo que este zig-zag sea muy beneficioso para los usuarios, así que deberíamos aterrizar en esta discusión. Si bien estoy de acuerdo con Morgan en que podría hacer pruebas más específicas, no me gusta descartar un comportamiento para dos versiones principales y luego volver a agregarlo. Creo que haría el menor ruido (arreglos para los clientes, preguntas / problemas en este rastreador) solo para revertir esta regresión.

Si bien entiendo el inconveniente, parece que hay una solución fácil con cambios mínimos en el código.

before(function() {
  window.google = 'This is a placeholder for sinon to overwrite.';
});

after(function() {
  delete window.google;
});

Esto permite que el código sinon no se modifique.

Regresión vs documentación deficiente

Este parece ser el comportamiento esperado ya que existen pruebas para ello. Deberíamos actualizar los documentos para reflejar eso en mi opinión. Llegó de manera importante, por lo que se toleran cambios importantes.

@fearphage Mantener el status quo significa que el stubing de campos no existentes es un comportamiento no admitido para sandboxes, mientras que es un comportamiento admitido para stubs normales. ¿No es un poco desafortunado que los dos conjuntos de funciones no se alineen?

La resolución se implementó en # 1557

He leído los diversos hilos y puedo ver por qué sucedió esto, pero es un verdadero problema en TypeScript, donde a menudo tienes funciones que se implementan en un prototipo de clase, en cuyo caso Sinon escupe el maniquí aunque todo se ve bien. sabio (ya que keyof YourType permitirá felizmente todas las funciones públicas que se definen más abajo en la cadena de prototipos).

Entiendo que TypeScript probablemente no sea una prioridad para ustedes, pero incluso en JS parece contrario a la intuición que myObject.callMe() se ejecutará perfectamente felizmente, mientras que sinon.stub(myObject, "callMe") no lo hará en ese caso. Preferiría no tener que ir a investigar cómo se armó ese objeto en particular solo para saber cómo cortarlo.

Realmente creo que este es un caso de uso importante para hacer un camino feliz, considerando que las clases están obteniendo más soporte nativo en JS.

Si obtiene un error que dice que el método no está definido en el objeto, sabrá que el error probablemente esté en el prototipo. Luego, modificar directamente el objeto usando myObject.callMe = sinon.stub(); no parece una gran molestia en mi humilde opinión ... También debería evitarle crear funciones de limpieza / desmontaje, ya que el prototipo nunca se cambió.

Sí, supongo que no es demasiado difícil solucionarlo, solo me está poniendo más carga cognitiva para ser consciente de cómo se implementan las cosas.

También parece inesperado, por lo que sentí la necesidad de agregar un comentario en la prueba para explicar por qué el código de código auxiliar en 2 líneas consecutivas para el mismo objeto era diferente y por qué estaba eliminando manualmente uno de los códigos auxiliares en mi desmontaje. pero el otro fue manejado por la caja de arena.

~ Gracias por restaurar este comportamiento. ~

Quería agregar un caso de uso que admita stubbing de propiedades inexistentes.

En mi caso de uso, estoy almacenando una propiedad en un objeto de configuración. El objeto de configuración tiene varias claves opcionales y se inicializa cargando un archivo desde la máquina del desarrollador. Cuando ejecuto una prueba en particular, necesito que una de esas claves se establezca en un valor conocido y luego quiero restaurar el objeto del desarrollador como estaba.

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue) es una forma muy clara de transmitir esto. Es bueno que obtengo el mismo comportamiento, aunque no sé en tiempo de ejecución si la clave está configurada o no.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

NathanHazout picture NathanHazout  ·  3Comentarios

brettz9 picture brettz9  ·  3Comentarios

zimtsui picture zimtsui  ·  3Comentarios

sudhirbits picture sudhirbits  ·  4Comentarios

fearphage picture fearphage  ·  4Comentarios