¿Cómo debo escribir una prueba para un método puro que no devuelve nada?

12

Tengo un montón de clases que tratan con la validación de valores. Por ejemplo, una clase RangeValidator verifica si un valor está dentro del rango especificado.

Cada clase de validador contiene dos métodos: is_valid(value) , que devuelve True o False según el valor, y ensure_valid(value) que verifica un valor específico y no hace nada si el valor es válido, o arroja una excepción específica si el valor no coincide con las reglas predefinidas.

Actualmente hay dos pruebas unitarias asociadas con este método:

  • El que pasa un valor no válido y garantiza que se lanzó la excepción.

    def test_outside_range(self):
        with self.assertRaises(demo.ValidationException):
            demo.RangeValidator(0, 100).ensure_valid(-5)
    
  • El que pasa un valor válido.

    def test_in_range(self):
        demo.RangeValidator(0, 100).ensure_valid(25)
    

Aunque la segunda prueba hace su trabajo, falla si se lanza la excepción y tiene éxito si ensure_valid no arroja nada: el hecho de que no haya assert s por dentro parece extraño. Alguien que lea tal código se preguntará inmediatamente por qué hay una prueba que parece para no hacer nada.

¿Es esta una práctica actual cuando se prueban métodos que no devuelven un valor y no tienen efectos secundarios? ¿O debería reescribir la prueba de una manera diferente? ¿O simplemente poner un comentario explicando lo que estoy haciendo?

    
pregunta Arseni Mourzenko 31.01.2017 - 22:29

4 respuestas

22

La mayoría de los marcos de prueba tienen una aserción explícita para "No lanzar", por ejemplo. Jasmine tiene expect(() => {}).not.toThrow(); y nUnit y los amigos también tienen uno.

    
respondido por el DeadMG 31.01.2017 - 22:53
7

Esto depende en gran medida del lenguaje y el marco utilizado. Hablando en términos de NUnit , hay métodos de Assert.Throws(...) . Puedes pasarles un método lambda:

Assert.Throws(() => rangeValidator.EnsureValid(-5))

que se ejecuta dentro de Assert.Throws . Lo más probable es que la llamada a la lambda esté envuelta por un bloque try { } catch { } y la afirmación falla, si se detecta una excepción.

Si su marco no proporciona estos medios, podría solucionarlo, envolviendo la llamada usted mismo (estoy escribiendo en C #):

// assert that the method does not fail
try
{
    rangeValidator.EnsureValid(50);
}
catch(Exception e)
{
    Assert.IsTrue(false, $"Exception: {e.Message}");
}

// assert that the method does fail
try
{
    rangeValidator.EnsureValid(50);
    Assert.IsTrue(false, $"Method is expected to throw an exception");
}
catch(Exception e)
{
}

Esto hace que la intención sea más clara, pero altera el código en cierta medida. (Por supuesto, puedes envolver todo esto en un método). Al final, dependerá de ti.

Editar

Como Doc Brown señaló en los comentarios, el problema no ha sido indicar que el método arroja, sino que no arroja. En NUnit también hay una aserción para eso

Assert.DoesNotThrow(() => rangeValidator.EnsureValid(-5))
    
respondido por el Paul Kertscher 01.02.2017 - 09:17
1

También puede afirmar que algunos métodos se llaman (o no se llaman) correctamente.

Por ejemplo:

public void SendEmail(User user)
     ... construct email ...
     _emailSender.Send(email);

En tu prueba:

emailSenderMock.VerifyIgnoreArgs(service =>
    service.Send(It.IsAny<Email>()),
    Times.Once
);
    
respondido por el Gabriel Robert 01.02.2017 - 13:37
0

Simplemente agrega un comentario para dejar en claro por qué no se necesita una afirmación y por qué no la has olvidado.

Como puede ver en las otras respuestas, cualquier otra cosa hace que el código sea más complicado y abarrotado. Con el comentario allí, otros programadores sabrán la intención de la prueba.

Dicho esto, este tipo de prueba debe ser una excepción (no es un juego de palabras). Si te encuentras escribiendo algo así con regularidad, entonces es probable que las pruebas te indiquen que el diseño no es óptimo.

    
respondido por el jhyot 01.02.2017 - 08:52

Lea otras preguntas en las etiquetas