¿Se considera que una prueba de Unidad es frágil si falla cuando cambia la lógica de negocio?

27

Por favor vea el código a continuación; prueba para ver si una persona con Sexo de mujer es elegible para la oferta1:

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id,"Offer1");
    Assert.False(offer1.IsEligible(person));
}

Esta prueba unitaria tiene éxito. Sin embargo, fallará si se ofrece 'Offer1' a las mujeres en el futuro.

¿Es aceptable decir que si la lógica empresarial que rodea la oferta 1 cambia, entonces la prueba unitaria debe cambiar? Tenga en cuenta que en algunos casos (para algunas ofertas) la lógica de negocios se cambia en la base de datos de esta manera:

update Offers set Gender='M' where offer=1;

y en algunos casos en el modelo de dominio como este:

if (Gender=Gender.Male)
{
  //do something
}

Tenga en cuenta también que, en algunos casos, la lógica de dominio detrás ofrece cambios regularmente y en otros casos no.

w0051977
fuente
2
Piense desde otro ángulo: ¿Desea tener pruebas que no fallaron cuando cambia la lógica en el sistema bajo la prueba?
Fabio

Respuestas:

77

Esto no es frágil en el sentido habitual. Una prueba unitaria se considera quebradiza si se rompe debido a cambios en la implementación que no afectan el comportamiento bajo prueba. Pero si la lógica de negocios en sí misma cambia, se supone que se rompe una prueba de esta lógica .

Dicho esto, si la lógica comercial cambia con frecuencia, quizás no sea apropiado codificar las expectativas en las pruebas unitarias. En su lugar, podría probar si las configuraciones en la base de datos afectan las ofertas como se esperaba.

El nombre de la prueba Returns False When Given A Person With A Gender Of Femaleno describe una regla comercial. Una regla de negocios sería algo así Offers Applicable to M should not be applied to persons of gender F.

Por lo tanto, podría escribir una prueba que confirme que si una oferta se define como solo aplicable a personas de tipo M, entonces una persona de tipo F no será indicada como elegible para ella. Esta prueba garantizará que la lógica funcione incluso si cambia la configuración de las ofertas específicas.

JacquesB
fuente
@JaquesB, ¿entonces no sería una prueba unitaria? o lo haría? Creo que sería una prueba de integración si la base de datos estuviera involucrada. ¿Está bien? ¿Está diciendo que no use pruebas unitarias si la lógica de negocios cambia mucho?
w0051977
@ w0051977: Depende de cómo escriba la prueba. Si la prueba incluye cambiar algo en una base de datos, entonces sería una prueba de integración.
JacquesB
3
@ w0051977 mejor idea: no permita que el repositorio sea una dependencia del componente responsable de implementar las reglas comerciales. Tenga una orquestación de nivel superior que llame al repositorio y luego invoque las reglas de negocio. Ahora puede probar las reglas de negocio de forma aislada.
Ant P
55
@ w0051977, por supuesto, lo es: las pruebas especifican el comportamiento. Si las reglas que gobiernan el comportamiento de un componente cambian, entonces las pruebas deben cambiar para reflejar el cambio en el comportamiento. Lo que no debería necesitar cambiar son pruebas que especifiquen comportamientos distintos de lo que está cambiando. Si la base de datos determina el comportamiento, una prueba que cubra algún otro código no tiene relación inherente y no debería necesitar cambiar a menos que la lógica de la base de datos esté dentro del alcance de la prueba. Ese alcance es para que usted lo defina y la semántica de si es una prueba unitaria o una prueba de integración no es realmente importante.
Ant P
3
@ w0051977 extendiendo esta idea un poco, si la lógica de negocios cambia o se corrige un error y las pruebas no necesitan ajustes, es una señal de que las pruebas no cubren suficientes casos y generalmente deberían expandirse.
Morgen
14

Cuando la propiedad se define en la base de datos de producción (o un clon para pruebas), esto no es una prueba unitaria . Una prueba unitaria verifica una unidad de trabajo y no requiere un estado externo particular para funcionar. Esto supone que Offer1se define en la base de datos como una oferta solo para hombres. Eso es un estado externo. Esto es más una prueba de integración , específicamente un sistema o una prueba de aceptación . Tenga en cuenta que las pruebas de aceptación a menudo no están programadas (no se ejecutan en un marco de prueba sino que se realizan manualmente por seres humanos).

Cuando la propiedad se define en el modelo de dominio con una ifdeclaración, la misma prueba es una prueba unitaria. Y puede ser frágil. Pero el verdadero problema es que el código es frágil. Como regla general, su código será más resistente si el comportamiento empresarial es configurable en lugar de codificado. Debido a que una implementación apresurada para corregir un pequeño error de codificación debería ser rara. Pero un requisito comercial que cambia sin previo aviso es solo un martes (algo que sucede semanalmente).

Puede estar utilizando un marco de prueba de unidad para ejecutar la prueba. Pero los marcos de pruebas unitarias no se limitan a ejecutar pruebas unitarias. También pueden ejecutar pruebas de integración.

Si estuviera escribiendo una prueba unitaria, crearía ambas persony offer1desde cero sin depender del estado de la base de datos. Algo como

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id, "ReturnsFalseWhenGivenAPersonWithAGenderOfFemale");
    offer1.markLimitedToGender("M");

    Assert.False(offer1.IsEligible(person));
}

Tenga en cuenta que esto no cambia en función de la lógica empresarial. No es afirmativo lo que offer1rechaza a las mujeres. Está haciendo offer1el tipo de oferta que rechaza a las mujeres.

Puede crear y configurar la base de datos como parte de la prueba. En C #, usando NUnit, o en JUnit de Java, configuraría la base de datos en un Setupmétodo. Presumiblemente su marco de prueba tiene una noción similar. En ese método, puede insertar registros en la base de datos con SQL.

Si le resulta difícil escribir código que sustituya una base de datos de prueba por la base de datos de producción, eso suena como una debilidad de prueba en su aplicación. Para las pruebas, sería mejor usar algo como inyección de dependencia que permita la sustitución. Luego, podría escribir pruebas que sean independientes de las reglas comerciales actuales.

Un beneficio adicional de esto es que a menudo es más fácil para el dueño del negocio (no necesariamente el dueño corporativo, más como la persona responsable de este producto en la jerarquía corporativa) configurar las reglas comerciales directamente. Porque si tiene este tipo de marco técnico, es fácil permitir que el propietario de la empresa use una interfaz de usuario (UI) para configurar la oferta. El propietario de la empresa seleccionaría la limitación en la interfaz de usuario y emitiría la markLimitedToGender("M")llamada. Luego, cuando la oferta persiste en la base de datos, la almacena. Pero no necesitaría almacenar la oferta para usarla. Por lo tanto, sus pruebas podrían crear y configurar una oferta que no existe en la base de datos.

En su sistema como se describe, el dueño del negocio tendría que presentar una solicitud al grupo técnico, que emitiría el SQL apropiado y actualizaría las pruebas. O el grupo técnico tiene que editar su código y pruebas (o pruebas y luego código). Ese parece un enfoque bastante pesado. Puedes hacerlo. Pero su software (no solo sus pruebas) sería menos frágil si no tuviera que hacerlo.

TL; DR : puede escribir pruebas como esta, pero es mejor que escriba su software para no tener que hacerlo.

mdfst13
fuente
Doy la libertad de mejorar algunos detalles menores en su respuesta. Por favor, compruebe si entendí bien sus intenciones.
Doc Brown
No hay ninguna indicación en la publicación original de que haya una base de datos involucrada. Por lo tanto, afirmar que supone que Offer1 ya está en la base de datos es extraño.
Winston Ewert
2
@ WinstonEwert: hay una indicación clara, tienes que leer la pregunta con más cuidado. Tampoco me di cuenta en la primera lectura, pero de hecho es de lo que habla el OP.
Doc Brown
@ mdfst13, lo siento, me perdí eso. Sin embargo, lo que dice el OP es que las condiciones a veces están en una base de datos y otras en el modelo de dominio. Su respuesta está perfectamente bien para el primer caso, e irreverentemente en el segundo. Si edita su respuesta para aclarar ese punto, eliminaré mi voto a toda prisa.
Winston Ewert