La mejor manera en que puedo expresar mi pregunta es con un ejemplo.
class foo{ constructor(size) { this.bar = new Array(size); } baz(index) { this.body[index] = true; } } Ignorando el constructor, supongamos que estamos realizando pruebas unitarias baz() . Se espera lógicamente que baz tome un index de entrada que esté entre 0 y bar.length - 1 , pero también se puede compilar lo siguiente:
baz(['hey'])baz(5) // with an array size of 2baz(true)baz('0')Ahora, he estado investigando mucho sobre lo que realmente estamos probando con pruebas unitarias, y la mejor respuesta que puedo obtener de lo que leo es que estamos probando el comportamiento de la función dentro de los límites de su contrato lógico con qué/quién lo llama. AKA, prueba la implementación mientras asume implícitamente que se llama correctamente .
Estoy completamente satisfecho con esto, pero lo que quiero saber es, ¿dónde trazamos esa línea? Ninguno de los ejemplos anteriores causará un error tipográfico, pero puedo ignorar 1,3 y probablemente 4 si sigo lo que aprendí anteriormente.
Pero, ¿debería preocuparme por 2? ¿O también asumo que el 'contrato' de la función se mantendrá no solo en términos de escritura implícita de las entradas, sino también en sus límites lógicos?
¿Debo probar y dar cuenta del ejemplo 2 en mi código?
Depende de si el contrato dice " ... y para todas las demás entradas, la función genera una excepción ", en cuyo caso debe probar que se genera como se esperaba, o si el contrato simplemente dice (o implica) " ... y no llámelo con cualquier otra cosa, es posible que no funcione o haga cosas impredecibles "(es decir , un comportamiento indefinido ), en cuyo caso no necesita probarlo.
Un número fuera del rango esperado (su caso de ejemplo 2) y un valor de un tipo inesperado (casos 1, 3, 4) pueden caer independientemente en cualquier lado de esa distinción. Puedes dibujar la línea donde quieras. Solo asegúrese de ganar puntos de bonificación al documentar dónde lo dibujó.