1 / 12
jul. 2017

Como suelo programar manteniendo las funciones lo más pequeñas posible (otro tema para hablar a parte) acabo con muchos pequeños métodos privados.

Claro, el cuerpo me pide crear test unitarios para todos ellos pero al ser privados es imposible y entiendo que hay que testearlos a través de los métodos públicos que los utilizan.

¿Este planteamiento es correcto?

Lo normal sería que quisieras testear ese método público que al final es el que se está exponiendo hacia fuera.

Que ese método público utilice internamente varios método privados da igual, es un detalle de implementación que no deberías tener en cuenta. Es más, imagina que haces un refactor de la implementación aunque la funcionalidad sigue siendo la misma, haber testeado unitariamente todas esas funciones privadas no sólo no te hubiera aportado gran cosa, encima te hubiera generado más trabajo de cara al refactor.

Eso si, intenta que ese método público no haga mil cosas :wink:

Hola,

La idea es no testear métodos privados ya que estos si cambian no deberían afectar a los métodos públicos, y los métodos públicos sí son los que se deberían testear. Eso sí, para asegurar que los métodos privados funcionan de forma indirecta los públicos deben hacer una única cosa, es decir, modificar un método privado no debe afectar al resultado esperado del test sobre el método público.

Ahora bien, si quieres hacer un test de un método privado puedes utilizar reflexión si el lenguaje te lo facilita.

Un ejemplo en PHP7. Existe su equivalente en Java.

Yo la verdad es no hago tests de métodos privados, pero en su momento me surgió la curiosidad.

Saludos.

Gracias. Leyendo vuestras respuestas creo que el problema lo tengo cuando el método público hace demasiadas cosas entonces el cuerpo me pide testear los métodos privados ya que son más concretos. Voy a trabajar en ello!

Reflexión no lo pensé pero si se me pasó por la cabeza cambiar la visibilidad de los métodos a "package" y meter los test dentro del mismo paquete :no_mouth:

La clave, ya apuntada en previas respuestas, es el grado de abstracción. Cuando Modesto se refiere a no testear detalles de implementación cabe preguntarse cómo decidir que es implementación y que es API. Así mismo, que grado de implementación, o incluso de oscuridad si se me permite, merece cierta pieza de código. Testear es consumir y dependiendo de la altura desde la que consumamos un método público (o un microservicio) puede ser considerado implementación; es una cuestión de perspectiva. Dependiendo del dominio y del estilo de programación que usemos puede tener sentido testear desde diferentes posiciones y 'esconder' implementaciones con brochas de diferente grosor.

Partiendo del ejemplo inicial, y asumiendo cierto desarrollo en el tiempo, un problema típico sería empezar con un método público que haga un porrón de cosas. Ante el horror observado el primer impulso pudiera ser 'barrer debajo de la alfombra', dividiendo ese método en varios privados. Si dejasemos ahí el refactor el código estaría más limpio en el sentido de lectura (y quizá ni eso, si los métodos privados son pírricos o los nombres son reguleros), pero no habríamos ganado mucho desde un punto de vista de modularidad, facilidad de evolución o testabilidad. En definitiva, seguiríamos teniendo tropecientos tests dirigiéndose al método público original.

En vez de preguntarse si es posible testear métodos privados cabría replantearse si:

  • Es momento de extraer cierta funcionalidad en otra abstracción (class, trait o lo que te ofrezca tu lenguaje)
  • Queremos testear trivialidades. No tiene sentido wrappear privadamente una llamada al SDK de turno con un nombre molón de nuestro dominio y cascarle un test, por si acaso. Esa desconfianza es naif e ignora los diversos costes de oportunidad en el testing.

Gracias por la ayuda a todos. Pongo un ejemplo concreto simplificado y en pseudocódigo, para que veáis más clara mi situación:

public List<Fichajes> obtenerHorarioFichajes(String nif, Date dia) {
  if(empleadoValido(nif) && fechaValida(fecha) {
    ...
  } else {
    ...
  }
}

  private boolean empleadoValida(String nif) {
    return nifValido(nif) && existeEmpleado(nif);
  }
    private boolean nifValido(String nif) { ... }
    private boolean existeEmpleado(String nif) { ... }

  private boolean fechaValido(Date fecha) {
    return fechaNoFutura(fecha) && fechaNoDemasiadoAntigua(fecha);
  }
    private fechaNoFutura(fecha) { ... }
    private fechaNoDemasiadoAntigua(fecha) { ... }

En este caso tendría que crear varios tests contra obtenerHorarioFichajes que "en realidad" están probando método privados que yo sé que existen:

@Test
horariosNifNovalido_lanzaError() {
  obtenerHorarioFichajes("xxxxxx", hoy);
} 

@Test
horariosEmpleadoNoExiste_lanzaError() {
  obtenerHoarioFichajes("12345678A");
}

@Test
horariosFechaFutura_lanzaError() {
 obtenerHorarioFichajes("3345432F", manyana);
}

y así... me siento raro, pienso "algo no va bien aquí..:" :confounded:

Gracias de nuevo!

Aquí veo 3 responsabilidades:

  • Obtener los horarios.
  • Obtener si la fecha es válida
  • Obtener si el empleado es válido

El método en sí debería limitarse al primer punto que es el que concuerda con su nombre.

¿Cómo? Asumiendo por contrato que el método va a recibir un empledo válido y una fecha válida. Es decir, esos deberían ser los parámetros, un EmpleadoValido y UnaFechaValida. Confias en los parámetros, por lo tanto hasta puedes eleminar el else.

El cliente ya sabría que va a facilitar una fecha válida y un empleado válido al método. Así ya sí puedes testear el método sin los métodos privados.

Incluso iría más hallá y me atrevería a decir que empleadoValido(nif) y fechaValida(fecha) no están en la clase correcta y podrían ser métodos públcos de otra clase o incluso una clase en sí misma, por ejemplo EmpleadoValido podría tener entidad en sí mismo.

Saludos.

Sí, lo he puesto así por no complicar el ejemplo. Quizás no es el mejor ejemplo del mundo pero entiendo lo que dices. Según tu opinión si tengo esa sensación de "intentar probar los métodos privados indirectamente" es que algo en el diseño de clases no anda bien. Esa es la duda que tengo y lo que intento aclarar.

Cuando me encuentre con un ejemplo apropiado y totalmente real lo comento.

¡Gracias!

He visto los primeros minutos del vídeo y me encanta. Parece que trata muchas dudas que se me han planteado. En casa con calma le echo un vistazo.

Gracias!

Sí, a eso me refería, a que si piensas que es importante testear esos métodos privados suele ser porque ese método publico tiene más responsabilidades de las que debería tener. Esos métodos privados, pensando en programación estructural dentro del método público, pueden tener todo el sentido del mundo, pero en términos de POO pueden ser métodos públicos de la misma clase, de otra clase, o incluso pueden llegar a crear una clase que se está ignorando porque sus métodos y variables estén repartidos por ahí.

Yo cuando me encuentro esta situación suelo pensar en términos de que la clase es una máquina con botones, es decir, puedes llamar al metodo1 público y luego según el resultado llamar al método 2 también público, o hacer una implementación con esos métodos diferente. Mientras que si el método2 lo consideras parte del método1 como privado estás atándote a una implementación concreta, y los metodos 1 y 2 pierden capacidad de ser reutilizados por otro objeto. De ahí que parezca que es importante testear ambos métodos, porque son responsabilidades diferentes.