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.