Pregunta

Suponga que tiene un método:

public void Save(Entity data)
{
    this.repositoryIocInstance.EntitySave(data);
}

¿Escribirías una prueba unitaria?

public void TestSave()
{
    // arrange
    Mock<EntityRepository> repo = new Mock<EntityRepository>();
    repo.Setup(m => m.EntitySave(It.IsAny<Entity>());

    // act
    MyClass c = new MyClass(repo.Object);
    c.Save(new Entity());

    // assert
    repo.Verify(m => EntitySave(It.IsAny<Entity>()), Times.Once());
}

Porque más adelante si cambias la implementación del método para hacer más '' complejo '' cosas como:

public void Save(Entity data)
{
    if (this.repositoryIocInstance.Exists(data))
    {
        this.repositoryIocInstance.Update(data);
    }
    else
    {
        this.repositoryIocInstance.Create(data);
    }
}

... su prueba unitaria fallaría pero probablemente no rompería su aplicación ...

Pregunta

¿Debería molestarme incluso en crear pruebas unitarias en métodos que no tienen ningún tipo de retorno * o ** no cambian nada fuera del simulacro interno ?

¿Fue útil?

Solución

Es cierto que su prueba depende de su implementación, que es algo que debe evitar (aunque a veces no es tan simple ...) y no es necesariamente malo. Pero se espera que este tipo de pruebas se rompan incluso si su cambio no rompe el código .

Podría tener muchos enfoques para esto:

  • Cree una prueba que realmente vaya a la base de datos y verifique si el estado cambió como se esperaba ( ya no será una prueba de unidad )
  • Cree un objeto de prueba que falsifique una base de datos y realice operaciones en la memoria (otra implementación para su repositorioIocInstance), y verifique que el estado haya cambiado como se esperaba. Los cambios en la interfaz del repositorio también incurrirían en cambios en este objeto. Pero sus interfaces no deberían estar cambiando mucho, ¿verdad?
  • Vea todo esto como demasiado costoso, y use su enfoque, que puede incurrir en romper innecesariamente las pruebas más tarde (pero una vez que la posibilidad es baja, está bien correr el riesgo)

Otros consejos

No olvide que las pruebas unitarias no son solo pruebas de código. Se trata de permitirle determinar cuándo cambia el comportamiento .

Entonces puede que tengas algo trivial. Sin embargo, su implementación cambia y puede tener un efecto secundario. Desea que su conjunto de pruebas de regresión le diga.

p. A menudo la gente dice que no debes probar a los setters / getters ya que son triviales. No estoy de acuerdo, no porque sean métodos complicados, sino que alguien puede cambiarlos inadvertidamente por ignorancia, escenarios de dedo gordo, etc.

Dado todo lo que acabo de decir, definitivamente implementaría pruebas para lo anterior (a través de la burla, y / o quizás valga la pena diseñar sus clases teniendo en cuenta la capacidad de prueba y hacer que informen el estado, etc.)

Hágase dos preguntas. "¿Cuál es el equivalente manual de esta prueba unitaria?" y ¿vale la pena automatizar? En su caso, sería algo así como:

¿Qué es el equivalente manual?  - iniciar depurador  - ingrese a "Guardar" método  - pase al siguiente, asegúrese de estar dentro de IRepository.EntitySave implementación

¿Vale la pena automatizar? Mi respuesta es "no". Es 100% obvio del código. De cientos de pruebas de desechos similares, no vi ninguna que resulte útil.

La regla general es, que usted prueba todas las cosas, que probablemente podrían romperse. Si está seguro de que el método es lo suficientemente simple (y sigue siendo lo suficientemente simple) como para no ser un problema, eso lo deja salir con las pruebas.

Lo segundo es que debe probar el contrato del método, no la implementación. Si la prueba falla después de un cambio, pero no la aplicación, entonces su prueba no es lo correcto. La prueba debe cubrir casos que son importantes para su aplicación. Esto debería garantizar que cada cambio en el método que no interrumpa la aplicación tampoco falle la prueba.

La respuesta breve a su pregunta es: , definitivamente debe probar métodos como ese.

Supongo que es importante que el método Save realmente guarde los datos. Si no escribe una prueba unitaria para esto, ¿cómo lo sabe?

Alguien más puede venir y eliminar esa línea de código que invoca el método EntitySave, y ninguna de las pruebas unitarias fallará. Más adelante, te preguntas por qué los artículos nunca se conservan ...

En su método, podría decir que cualquiera que elimine esa línea solo lo haría si tiene intenciones malignas, pero la cuestión es: las cosas simples no necesariamente siguen siendo simples, y es mejor escribir las pruebas unitarias antes de que las cosas se pongan complicado.

Es no un detalle de implementación que el método Save invoca a EntitySave en el repositorio; es parte del comportamiento esperado, y una parte bastante crucial, si puedo decirlo. Desea asegurarse de que los datos se estén guardando realmente.

El hecho de que un método no devuelva un valor no significa que no valga la pena probarlo. En general, si observa una buena Separación de Comando / Consulta (CQS), se debe esperar que cualquier método nulo cambie el estado de algo .

Algunas veces ese algo es la clase misma, pero otras veces, puede ser el estado de otra cosa. En este caso, cambia el estado del Repositorio, y eso es lo que debería probar.

Esto se denomina prueba de Salidas indeterminadas , en lugar de las Salidas directas (valores de retorno) más normales.

El truco consiste en escribir pruebas unitarias para que no se rompan con demasiada frecuencia. Cuando se usan simulacros, es fácil escribir accidentalmente Pruebas sobreespecificadas , razón por la cual la mayoría de los simulacros dinámicos (como Moq) pasa al modo Stub , donde realmente no importa cómo muchas veces invocas un método dado.

Todo esto, y mucho más, se explica en los excelentes xUnit Test Patterns .

Un método que no devuelve ningún resultado todavía cambia el estado de su aplicación. Su prueba de unidad, en este caso, debería probar si el nuevo estado es el previsto.

" su prueba unitaria fallaría pero probablemente no rompería su aplicación "

Esto es, en realidad, realmente importante saberlo. Puede parecer molesto y trivial, pero cuando alguien más comienza a mantener su código, puede haber hecho un cambio realmente malo en Guardar y (improbablemente) ha roto la aplicación.

El truco es priorizar.

Pruebe primero las cosas importantes. Cuando las cosas son lentas, agregue pruebas para cosas triviales.

Cuando no hay una afirmación en un método, esencialmente estás afirmando que no se lanzan excepciones.

También estoy luchando con la cuestión de cómo probar public void myMethod (). Supongo que si decide agregar un valor de retorno para la comprobabilidad, el valor de retorno debe representar todos los hechos relevantes necesarios para ver qué cambió sobre el estado de la aplicación.

public void myMethod()

se convierte

 public ComplexObject myMethod() { 
 DoLotsOfSideEffects()
 return new ComplexObject { rows changed, primary key, value of each column, etc };
 }

y no

public bool myMethod()  
  DoLotsOfSideEffects()
  return true;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top