¿Cómo ayuda el estilo funcional con las dependencias de burla?

8

De la entrevista con Kent Beck en un reciente número de la revista Java:

  

Binstock: Hablemos de microservicios. Me parece que la primera prueba en microservicios se complicaría en el sentido de que algunos servicios, para funcionar, necesitarán la presencia de un montón de otros servicios. ¿Estás de acuerdo?

     

Beck: Parece el mismo conjunto de intercambios sobre tener una clase grande o muchas clases pequeñas.

     

Binstock: Correcto, excepto que supongo que, aquí tienes que usar una gran cantidad de simulacros para poder configurar un sistema mediante el cual puedes probar un servicio determinado.

     

Beck: No estoy de acuerdo. Si está en un estilo imperativo, tienes   para usar un montón de burlas. En un estilo funcional donde las dependencias externas se reúnen en la parte superior de la cadena de llamadas, no creo que sea necesario. Creo que puedes obtener mucha cobertura de las pruebas unitarias.

¿Qué quiere decir? ¿Cómo puede el estilo funcional liberarte de burlarte de las dependencias externas?

    
pregunta Dan 17.01.2017 - 20:29

1 respuesta

8

Una función pura es una que:

  1. siempre dará el mismo resultado dados los mismos argumentos
  2. No tiene ningún efecto secundario observable (por ejemplo, cambios de estado)

Supongamos que estamos escribiendo algún código para manejar el inicio de sesión del usuario, donde queremos verificar que el nombre de usuario y la contraseña proporcionados sean correctos e impedir que el usuario inicie sesión si hay demasiados intentos fallidos. En un estilo imperativo, nuestro código podría tener este aspecto:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    if (user == null)
    {
        return false;
    }
    if (user.FailedAttempts > 3)
    {
        return false;
    }
    // Password hashing omitted for brevity
    if (user.Password != password)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return true;
}

Es bastante claro que esta no es una función pura:

  1. Esta función no siempre dará el mismo resultado para una combinación dada de username y password , ya que el resultado también depende del registro de usuario almacenado en la base de datos.
  2. La función puede cambiar el estado de la base de datos, es decir, tiene efectos secundarios.

También tenga en cuenta que para realizar una prueba unitaria de esta función necesitamos simular dos llamadas a la base de datos, FindUser y RecordFailedLoginAttempt .

Si tuviéramos que refactorizar este código en un estilo más funcional, podríamos terminar con algo como esto:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    var result = UserLoginPure(user, password);
    if (result == Result.FailedAttempt)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return result == Result.Success;
}

Result UserLoginPure(User user, string pasword)
{
    if (user == null)
    {
        return Result.UserNotFound;
    }
    if (user.FailedAttempts > 3)
    {
        return Result.LoginAttemptsExceeded;
    }
    if (user.Password != password)
    {
        return Result.FailedAttempt;        
    }
    return Result.Success;
}

Tenga en cuenta que aunque la función UserLogin aún no es pura, la función UserLoginPure ahora es una función pura y, como resultado, la lógica de autenticación del usuario central puede probarse por unidad sin necesidad de simular las dependencias externas. Esto se debe a que la interacción con la base de datos se maneja más arriba en la pila de llamadas.

    
respondido por el Justin 18.01.2017 - 00:43

Lea otras preguntas en las etiquetas