En el proceso de escribir un probador de mutaciones "Off By One" para mi marco favorito de pruebas de mutaciones (NinjaTurtles), escribí el siguiente código para brindar la oportunidad de verificar la corrección de mi implementación:Off By One errors and Mutation Testing
public int SumTo(int max)
{
int sum = 0;
for (var i = 1; i <= max; i++)
{
sum += i;
}
return sum;
}
ahora esto parece bastante simple, y no me dio la impresión de que no habría un problema al intentar mutar todas las constantes enteras literales en la IL. Después de todo, solo hay 3 (0
, 1
y ++
).
¡INCORRECTO!
Se hizo muy evidente en la primera ejecución que nunca iba a funcionar en esta instancia en particular. ¿Por qué? Porque al cambiar el código para
public int SumTo(int max)
{
int sum = 0;
for (var i = 0; i <= max; i++)
{
sum += i;
}
return sum;
}
solamente añade 0 (cero) a la suma, y esto, evidentemente, no tiene ningún efecto. Una historia diferente si fue el conjunto múltiple, pero en este caso no fue así.
Ahora hay un algoritmo bastante fácil para la elaboración de la suma de los números enteros
sum = max * (max + 1)/2;
que podría haber fallan las mutaciones fácilmente, ya que sumando o restando 1 de cualquiera de las constantes habrá resultado un error. (dado que max >= 0
)
Por lo tanto, problema resuelto para este caso en particular. Aunque no hizo lo que quería para la prueba de la mutación, que era comprobar lo que sucedería cuando perdiera el ++
, un bucle infinito. Pero ese es otro problema.
Por lo tanto - Mi Pregunta: ¿Hay casos triviales o no triviales en un bucle a partir de 0 o 1 puede resultar en una falla de la prueba "mutación fuera por uno" que no puede ser reprogramado (código bajo prueba o pruebas) ¿En una forma similar? (ejemplos, por favor)
Nota: Las pruebas de mutación fallan cuando el banco de pruebas pasa después de que se haya aplicado una mutación.
Actualización: un ejemplo de algo menos trivial, pero algo que todavía podría haber la prueba refactorizado de modo que no sería la siguiente prueba
public int SumArray(int[] array)
{
int sum = 0;
for (var i = 0; i < array.Length; i++)
{
sum += array[i];
}
return sum;
}
mutación en contra de este código podría fallar al cambiar el var i=0
a var i=1
si la entrada de prueba que le diste fue new[] {0,1,2,3,4,5,6,7,8,9}
. Sin embargo, cambie la entrada de prueba a new[] {9,8,7,6,5,4,3,2,1,0}
, y la prueba de mutación fallará. Entonces, un refactor exitoso prueba las pruebas.
Entonces, una tarea de código significativa, donde comenzar desde el índice 0 o 1 tiene el mismo efecto porque, como en el caso de suma, la operación en el índice 0 no tiene ningún efecto. Y en este caso, sin recurrir a una biblioteca externa para cálculos matriciales, no hay refactor, suponiendo que se le otorgue el uso de una matriz unidimensional. Interesante - reflexionando sobre esto. –
@evgeny - Acabo de terminar de entender que eres publicación original. Pero valió la pena. Tomé el C++, lo hice C# y jugué.Originalmente me quedé completamente perplejo en cuanto a cómo refactorizar para lograr un resultado que pudiera demostrarse mediante pruebas de mutación. Eventualmente, tuve el momento de mi bombilla y también se aplica a este ejemplo. Aplicar algo de inmutabilidad al método funciona de maravilla aquí. Entonces, en lugar de 'void', devuelve' char [] ', y tienes algún código comprobable, y posiblemente un método que sea eminentemente mejor para la experiencia. – pms1969
@ pms1969, a la derecha, la inmutabilidad ayuda aquí. Pero elimina la restricción "in situ" a los algoritmos. No creo que sea posible tratar el algoritmo, modificado de esta manera, como el mismo algoritmo. Para la transposición matricial existe un algoritmo no-in-situ muy simple, pero todos los algoritmos in-situ son bastante avanzados. Por cierto, acabo de agregar un ejemplo más, que ya es "inmutable". –