¿Por qué Mockito no se burla de los métodos estáticos?

267

Leí algunos hilos aquí sobre métodos estáticos, y creo que entiendo los problemas que puede causar el mal uso / uso excesivo de métodos estáticos. Pero realmente no llegué al fondo de por qué es difícil burlarse de los métodos estáticos.

Sé que otros frameworks burlones, como PowerMock, pueden hacer eso, pero ¿por qué no Mockito?

Leí este artículo , pero el autor parece estar religiosamente en contra de la palabra static, tal vez es mi pobre comprensión.

Una explicación / enlace fácil sería genial.

Abidi
fuente
12
Solo una nota al margen: PowerMock no es una biblioteca de objetos simulada per-se, solo agrega esas características (estadísticas de burla y ctors) en la parte superior de otras bibliotecas. Usamos PowerMock + Mockito en el trabajo, flotan bien entre ellos.
Matthias

Respuestas:

238

Creo que la razón puede ser que las bibliotecas de objetos simulados suelen crear simulacros creando dinámicamente clases en tiempo de ejecución (usando cglib ). Esto significa que implementan una interfaz en tiempo de ejecución (eso es lo que hace EasyMock si no me equivoco) o heredan de la clase para burlarse (eso es lo que hace Mockito si no me equivoco). Ambos enfoques no funcionan para miembros estáticos, ya que no puede anularlos usando la herencia.

La única forma de burlarse de las estadísticas es modificar el código de bytes de una clase en tiempo de ejecución, lo que supongo que es un poco más complicado que la herencia.

Esa es mi suposición, por lo que vale ...

Matías
fuente
77
Lo mismo es cierto para burlarse de los constructores por cierto. Esos, también, no se pueden cambiar a través de la herencia.
Matthias
11
También puede valer la pena agregar que algunos defensores de TDD / TBD perciben la falta de método estático y burla del constructor como algo bueno . Argumentan que cuando tienes que burlarte de métodos o constructores estáticos, entonces esto es un indicador de un diseño de clase pobre. Por ejemplo, al seguir un enfoque de IoC purista al ensamblar sus módulos de código, nunca tendrá la necesidad de burlarse de las estadísticas o los controladores en primer lugar (a menos que sean parte de algún componente de caja negra, por supuesto). Ver también giorgiosironi.blogspot.com/2009/11/…
Matthias
200
Creo que las herramientas de burla deberían darte lo que necesitas sin asumir que saben lo que es mejor para ti. Por ejemplo, si estaba usando una biblioteca de terceros que utilizaba una llamada a un método estático que necesitaba burlarme, sería bueno poder hacerlo. La idea de que un marco simulado no le proporcionará cierta capacidad porque se considera un mal diseño es fundamentalmente defectuoso.
Lo-Tan
11
@ Lo-Tan: es como decir que un idioma debe ser capaz de todo, sin asumir que sabe mejor que tú. Eso es solo vanidad de tu parte, porque parecen ser imponentes. El problema aquí es que la batalla "anti / pro estática" no está clara, al igual que los marcos. Estoy de acuerdo en que deberíamos tener ambos. Pero donde los hechos son claros, prefiero un marco que los imponga . Esa es una forma de aprender: herramientas que lo mantienen en el camino. Entonces usted mismo no tiene que hacerlo. Pero ahora cada cabeza de fideo puede imponer su llamado "buen diseño". "Fundamentalmente defectuoso" ...
nevvermind
13
@nevvermind Eh? Un lenguaje de alto nivel está destinado a ayudarte y tener las abstracciones necesarias para que puedas concentrarte en las piezas importantes. Una biblioteca de pruebas es una herramienta, una herramienta que uso para producir código de mejor calidad y, con suerte, mejor diseñado. ¿Cuál es el punto de una biblioteca de prueba / simulación cuando tiene limitaciones que pueden significar que no puedo usarla cuando tengo que integrar el código mal diseñado de otra persona? No parece bien pensado, mientras que los buenos idiomas lo han sido .
Lo-Tan
28

Si necesita burlarse de un método estático, es un fuerte indicador de un mal diseño. Por lo general, se burla de la dependencia de su clase bajo prueba. Si su clase bajo prueba se refiere a un método estático, como java.util.Math # sin, por ejemplo, significa que la clase bajo prueba necesita exactamente esta implementación (de precisión frente a velocidad, por ejemplo). Si quiere abstraerse de una implementación sinusal concreta, probablemente necesite una interfaz (¿ve a dónde va esto?)

ene
fuente
3
Bueno, utilicé métodos estáticos para proporcionar abstracciones de alto nivel, como una "fachada de persistencia estática". Tal fachada mantiene el código del cliente alejado de las complejidades y detalles de bajo nivel de una API ORM, proporcionando una API más consistente y fácil de usar, al tiempo que permite mucha flexibilidad.
Rogério
¿Y por qué tienes que burlarte? Si depende del método estático, su "unidad" o "módulo" no es solo la clase sino que también incluye la "fachada de persistencia estática".
Jan
88
Es cierto, pero a veces puede que no tenga otra opción si, por ejemplo, necesita burlarse de un método estático que se encuentra en alguna clase de terceros.
Stijn Geukens
66
Es cierto, pero a veces podemos estar lidiando con singletons.
Manu Manjunath
El único pensamiento que no se puede resolver mediante la abstracción es demasiados niveles de abstracción ... Agregar capas de abstracción agrega complejidad y, a menudo, es innecesario. Pienso en los ejemplos que he visto de marcos que intentan burlarse de System.currentTimeMillis () envolviendo esta simple llamada en una sola clase. Terminamos con una clase singleton por método en lugar de simplemente tener métodos, solo para facilitar las pruebas. Y luego, cuando presenta un equipo de terceros que llama al método estático directo en lugar de a través de su contenedor singleton, las pruebas fallan de todos modos ...
P. Jeremy Krieg
5

En serio, creo que es olor a código si también necesitas burlarte de los métodos estáticos.

  • ¿Métodos estáticos para acceder a funcionalidades comunes? -> Use una instancia singleton e inyecte eso
  • Código de terceros? -> Envuélvelo en tu propia interfaz / delegado (y si es necesario, conviértelo también en singleton)

La única vez que esto me parece excesivo es libs como Guava, pero de todos modos no deberías necesitar burlarte de este tipo porque es parte de la lógica ... (cosas como Iterables.transform (..)) De
esa manera tu propio código se mantiene limpio, puede burlarse de todas sus dependencias de manera limpia y tiene una capa anticorrupción contra dependencias externas. He visto PowerMock en la práctica y todas las clases para las que lo necesitábamos estaban mal diseñadas. Además, la integración de PowerMock a veces causó serios problemas
(por ejemplo, https://code.google.com/p/powermock/issues/detail?id=355 )

PD: Lo mismo vale para los métodos privados, también. No creo que las pruebas deban conocer los detalles de los métodos privados. Si una clase es tan compleja que tienta a burlarse de métodos privados, probablemente sea una señal de dividir esa clase ...

pete83
fuente
2
Singleton hará que te encuentres con todo tipo de problemas, particularmente cuando te des cuenta de que realmente necesitas más de una instancia y ahora necesitas refactorizar todo tu sistema para que eso suceda.
Ricardo Freitas
No dije que recomiendo el patrón Singleton a todos. Lo que quise decir es que, si tengo que decidir entre una clase de utilidad estática y un Singleton que ofrece la misma funcionalidad, elegiría el Singleton. Y si una clase es Singleton o no debería ser controlada por el marco DI de todos modos, en mi clase I @Inject SomeDependencyy en mi configuración que defino bind(SomeDependency.class).in(Singleton.class). Por lo tanto, si mañana ya no es un Singleton, cambio la configuración y ya está.
pete83
@ pete83 Te escucho hermano. Sin embargo, tengo un problema con las bibliotecas de prueba o los marcos que requieren que los desarrolladores cambien su diseño para cumplir con los límites / diseño del marco de prueba. Eso es IMO poniendo el carro delante del caballo, o la cola moviendo al perro.
Matt Campbell
1
Ese argumento tiene poco sentido para mí. Los patrones Singleton han caído en desgracia desde hace años, por demasiadas razones para enumerarlos aquí. ¿Qué constituye el código "limpio"? Si tengo un método de instancia de clase que llama a un método auxiliar estático que devuelve alguna operación de E / S, ¿por qué no querría que se burlen de eso en una prueba? ¿Y cómo es ese pobre diseño? Todos estos métodos estáticos que se retuercen a mano no cuadran. Burlarse de un método es lo opuesto a probarlo. Si es demasiado difícil de implementar, simplemente dígalo y
terminemos
Oh hombre, nunca estaba hablando de ese patrón Singleton de la vieja escuela donde todos llaman a Foo.getInstance()todas partes. Acabo de escribir singleton en la respuesta para contrarrestar el argumento "pero un método estático no requiere la creación de muchos objetos wrapper". También conceptualmente para mí hay poca diferencia entre un método estático y un método de instancia en un singleton, solo que no puedes burlarte de este colaborador singleton. Pero singleton o no no es absolutamente el punto que estaba tratando de hacer, el punto es inyectar y burlarse de los colaboradores y no llamar a métodos estáticos si dificulta las pruebas.
pete83
4

Mockito devuelve objetos, pero static significa "nivel de clase, no nivel de objeto". Mockito dará una excepción de puntero nulo para static.

salsinga
fuente
0

En algunos casos, los métodos estáticos pueden ser difíciles de probar, especialmente si necesitan ser burlados, razón por la cual la mayoría de los marcos de burla no son compatibles. Encontré que esta publicación de blog es muy útil para determinar cómo burlarse de los métodos y clases estáticos.

Tyler Treat
fuente
1
La burla de los métodos estáticos es aún más fácil que la burla de los métodos de instancias (ya que no hay ninguna instancia), cuando se utiliza una API de burla adecuada.
Rogério
Esto es como responder la pregunta con la pregunta en sí, que era por qué es difícil hacerlo, para lo cual esto no es una respuesta.
Matthias
40
Lo rechacé porque la publicación del blog recomienda una solución costosa (refactorizar el código de producción), en lugar de resolver el problema de aislar una clase de los métodos estáticos que utiliza. OMI, una herramienta de burla que realmente hace el trabajo no discriminaría los métodos de ningún tipo; un desarrollador debe ser libre de decidir si el uso de métodos estáticos es bueno o malo en una situación dada, en lugar de verse obligado a seguir un camino.
Rogério