He leído esta publicación sobre cómo probar métodos privados. Por lo general, no los pruebo, porque siempre pensé que es más rápido probar solo los métodos públicos que se llamarán desde fuera del objeto. ¿Pruebas métodos privados? ¿Debería probarlos siempre?
unit-testing
testing
language-agnostic
Patrick Desjardins
fuente
fuente
Respuestas:
No hago pruebas unitarias de métodos privados. Un método privado es un detalle de implementación que debe ocultarse a los usuarios de la clase. Probar métodos privados rompe la encapsulación.
Si encuentro que el método privado es enorme o complejo o lo suficientemente importante como para requerir sus propias pruebas, simplemente lo pongo en otra clase y lo hago público allí ( Object Object ). Entonces puedo probar fácilmente el método anteriormente privado, pero ahora público, que ahora vive en su propia clase.
fuente
¿Cuál es el propósito de la prueba?
La mayoría de las respuestas hasta ahora dicen que los métodos privados son detalles de implementación que no (o al menos no deberían) importar siempre que la interfaz pública esté bien probada y funcione. Eso es absolutamente correcto si su único propósito para las pruebas es garantizar que la interfaz pública funcione .
Personalmente, mi uso principal para las pruebas de código es asegurar que los cambios futuros en el código no causen problemas y ayudar a mis esfuerzos de depuración si lo hacen. Me parece que probar los métodos privados tan a fondo como la interfaz pública (¡si no más!) Promueve ese propósito.
Considere: Tiene un método público A que llama al método privado B. A y B utilizan el método C. C es cambiado (quizás por usted, quizás por un proveedor), lo que hace que A comience a fallar sus pruebas. ¿No sería útil tener pruebas para B también, a pesar de que es privado, para saber si el problema está en el uso de C por parte de A, el uso de C por parte de B o ambos?
Probar métodos privados también agrega valor en casos donde la cobertura de prueba de la interfaz pública es incompleta. Si bien esta es una situación que generalmente queremos evitar, la prueba de la unidad de eficiencia depende tanto de las pruebas para encontrar errores como de los costos asociados de desarrollo y mantenimiento de esas pruebas. En algunos casos, los beneficios de una cobertura de prueba del 100% pueden considerarse insuficientes para garantizar los costos de esas pruebas, lo que genera brechas en la cobertura de prueba de la interfaz pública. En tales casos, una prueba bien dirigida de un método privado puede ser una adición muy efectiva a la base del código.
fuente
testDoSomething()
otestDoSomethingPrivate()
. Esto hace que la prueba sea menos valiosa. . Aquí hay más razones para probar private stackoverflow.com/questions/34571/… :Tiendo a seguir los consejos de Dave Thomas y Andy Hunt en su libro Pragmatic Unit Testing :
Pero a veces no puedo evitar probar métodos privados porque me da la sensación de que estoy construyendo un programa completamente robusto.
fuente
Me siento obligado a probar funciones privadas ya que sigo cada vez más una de nuestras últimas recomendaciones de control de calidad en nuestro proyecto:
Ahora el efecto secundario de la aplicación de esta política es que muchas de mis funciones públicas muy grandes se dividen en muchas funciones privadas más enfocadas y mejor nombradas .
La función pública todavía está allí (por supuesto), pero se reduce esencialmente a todas esas "subfunciones" privadas.
Eso es realmente genial, porque la pila de llamadas ahora es mucho más fácil de leer (en lugar de un error dentro de una función grande, tengo un error en una subfunción secundaria con el nombre de las funciones anteriores en la pila de llamadas para ayudarme a comprender 'cómo llegué allí')
Sin embargo, ahora parece más fácil probar directamente esas funciones privadas y dejar la prueba de la gran función pública a algún tipo de prueba de 'integración' donde un escenario necesita ser abordado.
Solo mis 2 centavos.
fuente
Sí, pruebo funciones privadas, porque aunque se prueban con sus métodos públicos, es bueno en TDD (Test Driven Design) probar la parte más pequeña de la aplicación. Pero las funciones privadas no son accesibles cuando está en su clase de unidad de prueba. Esto es lo que hacemos para probar nuestros métodos privados.
¿Por qué tenemos métodos privados?
Las funciones privadas existen principalmente en nuestra clase porque queremos crear código legible en nuestros métodos públicos. No queremos que el usuario de esta clase llame a estos métodos directamente, sino a través de nuestros métodos públicos. Además, no queremos cambiar su comportamiento al extender la clase (en caso de estar protegido), por lo tanto, es privado.
Cuando codificamos, utilizamos el diseño controlado por prueba (TDD). Esto significa que a veces nos topamos con una funcionalidad que es privada y queremos probar. Las funciones privadas no son verificables en phpUnit, porque no podemos acceder a ellas en la clase Test (son privadas).
Creemos que aquí hay 3 soluciones:
1. Puede probar sus partes privadas a través de sus métodos públicos
Ventajas
Desventajas
2. Si lo privado es tan importante, entonces quizás sea un código para crear una nueva clase separada para él
Ventajas
Desventajas
3. Cambie el modificador de acceso a (final) protegido
Ventajas
Desventajas
Ejemplo
Entonces nuestra unidad de prueba ahora puede llamar a test_sleepWithSuspect para probar nuestra función privada anterior.
fuente
No me gusta probar la funcionalidad privada por un par de razones. Son los siguientes (estos son los puntos principales para las personas TLDR):
Explicaré cada uno de estos con un ejemplo concreto. Resulta que 2) y 3) están algo intrincadamente conectados, por lo que su ejemplo es similar, aunque considero que son razones separadas por las que no debe probar métodos privados.
Hay momentos en los que es apropiado probar métodos privados, solo es importante tener en cuenta los inconvenientes mencionados anteriormente. Voy a repasarlo con más detalle más adelante.
También repaso por qué TDD no es una excusa válida para probar métodos privados al final.
Refactorizando su salida de un mal diseño
Uno de los patrones (anti) más comunes que veo es lo que Michael Feathers llama una clase "Iceberg" (si no sabe quién es Michael Feathers, vaya a comprar / lea su libro "Trabajando eficazmente con el código heredado". Él es una persona que vale la pena conocer si es un ingeniero / desarrollador de software profesional). Hay otros patrones (anti) que hacen que este problema surja, pero este es, con mucho, el más común con el que me he encontrado. Las clases "Iceberg" tienen un método público, y el resto son privadas (por eso es tentador probar los métodos privados). Se llama clase "Iceberg" porque generalmente hay un método público solitario que aparece, pero el resto de la funcionalidad está oculta bajo el agua en forma de métodos privados.
Por ejemplo, es posible que desee probar
GetNextToken()
llamándolo sucesivamente a una cadena y observando que devuelve el resultado esperado. Una función como esta garantiza una prueba: ese comportamiento no es trivial, especialmente si sus reglas de tokenización son complejas. Supongamos que no es tan complejo, y solo queremos enredar en tokens delimitados por el espacio. Entonces escribes una prueba, tal vez se ve más o menos así (algún código psuedo agnóstico de lenguaje, espero que la idea sea clara)Bueno, eso en realidad se ve muy bien. Queremos asegurarnos de mantener este comportamiento a medida que hacemos cambios. ¡Pero
GetNextToken()
es una función privada ! Por lo tanto, no podemos probarlo de esta manera, ya que ni siquiera se compilará (suponiendo que estemos usando algún lenguaje que realmente aplique lo público / privado, a diferencia de algunos lenguajes de script como Python). Pero, ¿qué hay de cambiar laRuleEvaluator
clase para seguir el Principio de responsabilidad única (Principio de responsabilidad única)? Por ejemplo, parece que tenemos un analizador, un tokenizador y un evaluador agrupados en una clase. ¿No sería mejor separar esas responsabilidades? Además de eso, si crea unaTokenizer
clase, entonces sus métodos públicos seríanHasMoreTokens()
yGetNextTokens()
. losRuleEvaluator
clase podría tener unTokenizer
objeto como miembro. Ahora, podemos mantener la misma prueba anterior, excepto que estamos probando laTokenizer
clase en lugar de laRuleEvaluator
clase.Así es como se vería en UML:
Tenga en cuenta que este nuevo diseño aumenta la modularidad, por lo que podría reutilizar estas clases en otras partes de su sistema (antes de que no pudiera, los métodos privados no son reutilizables por definición). Esta es la principal ventaja de romper el RuleEvaluator, junto con una mayor comprensión / localidad.
La prueba se vería extremadamente similar, excepto que en realidad se compilaría esta vez ya que el
GetNextToken()
método ahora es público en laTokenizer
clase:Probar componentes privados a través de una interfaz pública y evitar la duplicación de pruebas
Incluso si no cree que puede dividir su problema en menos componentes modulares (que puede hacer el 95% del tiempo si solo intenta hacerlo), simplemente puede probar las funciones privadas a través de una interfaz pública. Muchas veces no vale la pena probar a los miembros privados porque serán probados a través de la interfaz pública. Muchas veces lo que veo son pruebas que se ven muy , pero prueban dos funciones / métodos diferentes. Lo que termina sucediendo es que cuando los requisitos cambian (y siempre lo hacen), ahora tiene 2 pruebas rotas en lugar de 1. Y si realmente probó todos sus métodos privados, podría tener más de 10 pruebas rotas en lugar de 1. o hacer ellos públicos o usando la reflexión) que de otra manera podrían ser probados a través de una interfaz pública pueden causar duplicación de prueba En resumen , probar funciones privadas (mediante
FRIEND_TEST
. Realmente no quieres esto, porque nada duele más que tu conjunto de pruebas que te frena. ¡Se supone que disminuye el tiempo de desarrollo y los costos de mantenimiento! Si prueba métodos privados que de otro modo se prueban a través de una interfaz pública, el conjunto de pruebas puede hacer lo contrario y aumentar activamente los costos de mantenimiento y el tiempo de desarrollo. Cuando haces pública una función privada, o si usas algo comoFRIEND_TEST
y / o reflexión, generalmente terminará arrepintiéndose a la larga.Considere la siguiente implementación posible de la
Tokenizer
clase:Digamos que
SplitUpByDelimiter()
es responsable de devolver una matriz de manera que cada elemento de la matriz sea un token. Además, digamos queGetNextToken()
es simplemente un iterador sobre este vector. Entonces su prueba pública podría verse así:Supongamos que tenemos lo que Michael Feather llama una herramienta a tientas . Esta es una herramienta que te permite tocar las partes privadas de otras personas. Un ejemplo es
FRIEND_TEST
de googletest, o reflexión si el idioma lo admite.Bueno, ahora digamos que los requisitos cambian, y la tokenización se vuelve mucho más compleja. Decide que un delimitador de cadena simple no será suficiente y necesita una
Delimiter
clase para manejar el trabajo. Naturalmente, esperará que se rompa una prueba, pero ese dolor aumenta cuando prueba funciones privadas.¿Cuándo pueden ser apropiados los métodos privados de prueba?
No hay una "talla única para todos" en el software. A veces está bien (y en realidad es ideal) "romper las reglas". Recomiendo no probar la funcionalidad privada cuando pueda. Hay dos situaciones principales cuando creo que está bien:
He trabajado mucho con sistemas heredados (por eso soy tan fanático de Michael Feathers), y puedo decir con seguridad que a veces es más seguro simplemente probar la funcionalidad privada. Puede ser especialmente útil para obtener "pruebas de caracterización" en la línea de base.
Tienes prisa y tienes que hacer lo más rápido posible aquí y ahora. A la larga, no desea probar métodos privados. Pero diré que generalmente toma algún tiempo refactorizar para abordar los problemas de diseño. Y a veces tienes que enviar en una semana. Eso está bien: haz lo rápido y sucio y prueba los métodos privados usando una herramienta de búsqueda a tientas si eso es lo que crees que es la forma más rápida y confiable de hacer el trabajo. Pero comprenda que lo que hizo fue subóptimo a largo plazo, y considere volver a hacerlo (o, si se olvidó pero lo ve más tarde, corríjalo).
Probablemente hay otras situaciones en las que está bien. Si crees que está bien y tienes una buena justificación, entonces hazlo. Nadie te está deteniendo. Solo tenga en cuenta los costos potenciales.
La excusa TDD
Por otro lado, realmente no me gusta la gente que usa TDD como excusa para probar métodos privados. Practico TDD, y no creo que TDD te obligue a hacerlo. Puede escribir su prueba (para su interfaz pública) primero, y luego escribir código para satisfacer esa interfaz. A veces escribo una prueba para una interfaz pública, y la satisfaré escribiendo uno o dos métodos privados más pequeños (pero no pruebo los métodos privados directamente, pero sé que funcionan o mi prueba pública no funcionará) ) Si necesito probar casos extremos de ese método privado, escribiré un montón de pruebas que los afectarán a través de mi interfaz pública.Si no puede descubrir cómo llegar a los casos límite, esta es una buena señal de que necesita refactorizar en componentes pequeños, cada uno con sus propios métodos públicos. Es una señal de que sus funciones privadas están haciendo demasiado, y fuera del alcance de la clase .
Además, a veces encuentro que escribo una prueba que es demasiado grande para masticar en este momento, por lo que pienso "eh volveré a esa prueba más tarde cuando tenga más API para trabajar" (I Lo comentaré y lo mantendré en el fondo de mi mente). Aquí es donde muchos desarrolladores que he conocido comenzarán a escribir pruebas para su funcionalidad privada, utilizando TDD como chivo expiatorio. Dicen "oh, bueno, necesito otra prueba, pero para escribir esa prueba, necesitaré estos métodos privados. Por lo tanto, como no puedo escribir ningún código de producción sin escribir una prueba, necesito escribir una prueba por un método privado ". Pero lo que realmente necesitan hacer es refactorizar en componentes más pequeños y reutilizables en lugar de agregar / probar un montón de métodos privados a su clase actual.
Nota:
Respondí una pregunta similar acerca de probar métodos privados usando GoogleTest hace un tiempo. Principalmente modifiqué esa respuesta para que sea más independiente del lenguaje aquí.
PD: Aquí está la conferencia relevante sobre las clases de iceberg y las herramientas a tientas de Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU
fuente
_
, indica "oye, esto es 'privado'. Puede usarlo, pero la divulgación completa, no fue diseñado para su reutilización y solo debe usarlo si realmente saber lo que estás haciendo ". Puede adoptar el mismo enfoque en cualquier idioma: haga públicos esos miembros, pero márquelos con un encabezado_
. O tal vez esas funciones realmente deberían ser privadas y solo probadas a través de una interfaz pública (consulte la respuesta para obtener más detalles). Es caso por caso, no hay regla generalCreo que es mejor probar la interfaz pública de un objeto. Desde el punto de vista del mundo exterior, solo importa el comportamiento de la interfaz pública y esto es a lo que deben dirigirse las pruebas de su unidad.
Una vez que haya escrito algunas pruebas unitarias sólidas para un objeto, no querrá tener que volver atrás y cambiar esas pruebas solo porque la implementación detrás de la interfaz cambió. En esta situación, ha arruinado la consistencia de sus pruebas unitarias.
fuente
Si su método privado no se prueba llamando a sus métodos públicos, ¿qué está haciendo? Estoy hablando privado no protegido o amigo.
fuente
Si el método privado está bien definido (es decir, tiene una función que es comprobable y no está destinada a cambiar con el tiempo), entonces sí. Pruebo todo lo que es comprobable donde tiene sentido.
Por ejemplo, una biblioteca de cifrado puede ocultar el hecho de que realiza un cifrado en bloque con un método privado que cifra solo 8 bytes a la vez. Escribiría una prueba unitaria para eso: no está destinado a cambiar, aunque esté oculto, y si se rompe (debido a futuras mejoras de rendimiento, por ejemplo), quiero saber que es la función privada la que se rompió, no solo que una de las funciones públicas se rompió.
Acelera la depuración más tarde.
-Adán
fuente
Si está desarrollando una prueba de manejo (TDD), probará sus métodos privados.
fuente
No soy un experto en este campo, pero las pruebas unitarias deberían probar el comportamiento, no la implementación. Los métodos privados son estrictamente parte de la implementación, por lo que en mi humilde opinión no se debe probar.
fuente
Probamos métodos privados por inferencia, con lo que quiero decir que buscamos una cobertura de prueba de clase total de al menos el 95%, pero solo hacemos que nuestras pruebas recurran a métodos públicos o internos. Para obtener la cobertura, necesitamos hacer múltiples llamadas al público / internos basados en los diferentes escenarios que pueden ocurrir. Esto hace que nuestras pruebas sean más intensas en torno al propósito del código que están probando.
La respuesta de Trumpi a la publicación que ha vinculado es la mejor.
fuente
Las pruebas unitarias, creo, son para probar métodos públicos. Sus métodos públicos usan sus métodos privados, por lo que indirectamente también se están probando.
fuente
He estado lidiando con este problema durante un tiempo, especialmente al intentar suerte en TDD.
Me he encontrado con dos publicaciones que creo que abordan este problema con suficiente profundidad en el caso de TDD.
En resumen:
Cuando se utilizan técnicas de desarrollo (diseño) basadas en pruebas, los métodos privados deben surgir solo durante el proceso de refactorización de código ya en funcionamiento y probado.
Por la naturaleza misma del proceso, cualquier parte de la funcionalidad de implementación simple extraída de una función completamente probada será autoevaluada (es decir, cobertura de prueba indirecta).
Para mí, parece bastante claro que en la parte inicial de la codificación, la mayoría de los métodos serán funciones de nivel superior porque están encapsulando / describiendo el diseño.
Por lo tanto, estos métodos serán públicos y probarlos será bastante fácil.
Los métodos privados vendrán más tarde una vez que todo esté funcionando bien y tengamos en cuenta la legibilidad y la limpieza .
fuente
Como se citó anteriormente, "Si no prueba sus métodos privados, ¿cómo sabe que no se romperán?"
Este es un problema importante. Uno de los grandes puntos de las pruebas unitarias es saber dónde, cuándo y cómo algo se rompió lo antes posible. Disminuyendo así una cantidad significativa de desarrollo y esfuerzo de control de calidad. Si todo lo que se prueba es el público, entonces no tiene una cobertura honesta y una delineación de los elementos internos de la clase.
He descubierto que una de las mejores formas de hacerlo es simplemente agregar la referencia de prueba al proyecto y poner las pruebas en una clase paralela a los métodos privados. Ponga la lógica de compilación adecuada para que las pruebas no se incorporen al proyecto final.
Entonces tiene todos los beneficios de probar estos métodos y puede encontrar problemas en segundos frente a minutos u horas.
Entonces, en resumen, sí, pruebe sus métodos privados.
fuente
No deberías . Si sus métodos privados tienen suficiente complejidad que deben probarse, debe colocarlos en otra clase. Mantenga una alta cohesión , una clase debe tener un solo propósito. La interfaz pública de clase debería ser suficiente.
fuente
Si no prueba sus métodos privados, ¿cómo sabe que no se romperán?
fuente
Obviamente depende del idioma. En el pasado con c ++, he declarado que la clase de prueba es una clase amiga. Desafortunadamente, esto requiere su código de producción para saber acerca de la clase de prueba.
fuente
Entiendo el punto de vista donde los métodos privados se consideran detalles de implementación y luego no tienen que ser probados. Y me apegaría a esta regla si tuviéramos que desarrollarnos solo fuera del objeto. Pero nosotros, ¿somos algún tipo de desarrolladores restringidos que están desarrollando solo fuera de los objetos, llamando solo a sus métodos públicos? ¿O en realidad también estamos desarrollando ese objeto? Como no estamos obligados a programar objetos externos, probablemente tendremos que llamar a esos métodos privados a los nuevos públicos que estamos desarrollando. ¿No sería genial saber que el método privado resiste contra viento y marea?
Sé que algunas personas podrían responder que si estamos desarrollando otro método público en ese objeto, entonces este debería probarse y eso es todo (el método privado podría seguir viviendo sin prueba). Pero esto también es cierto para cualquier método público de un objeto: al desarrollar una aplicación web, todos los métodos públicos de un objeto se invocan desde los métodos de los controladores y, por lo tanto, podrían considerarse como detalles de implementación para los controladores.
Entonces, ¿por qué estamos uniendo objetos de prueba? Porque es realmente difícil, por no decir imposible, estar seguros de que estamos probando los métodos de los controladores con la entrada adecuada que activará todas las ramas del código subyacente. En otras palabras, cuanto más alto estamos en la pila, más difícil es probar todo el comportamiento. Y lo mismo ocurre con los métodos privados.
Para mí, la frontera entre los métodos públicos y privados es un criterio psicológico cuando se trata de pruebas. Los criterios que más me importan son:
fuente
Si encuentro que el método privado es enorme o complejo o lo suficientemente importante como para requerir sus propias pruebas, simplemente lo pongo en otra clase y lo hago público allí (Object Object). Entonces puedo probar fácilmente el método anteriormente privado pero ahora público que ahora vive en su propia clase.
fuente
Nunca entiendo el concepto de Prueba de Unidad, pero ahora sé cuál es el objetivo.
Una prueba unitaria no es una prueba completa . Por lo tanto, no es un reemplazo para el control de calidad y la prueba manual. El concepto de TDD en este aspecto es incorrecto, ya que no se puede probar todo, incluidos los métodos privados, sino también los métodos que utilizan recursos (especialmente recursos que no tenemos control). TDD basa toda su calidad en algo que no se pudo lograr.
Una prueba de unidad es más una prueba de pivote . Marca un pivote arbitrario y el resultado del pivote debe permanecer igual.
fuente
Público versus privado no es una distinción útil para qué apis llamar de sus pruebas, ni método vs. clase. La mayoría de las unidades comprobables son visibles en un contexto, pero están ocultas en otros.
Lo que importa es la cobertura y los costos. Debe minimizar los costos mientras logra los objetivos de cobertura de su proyecto (línea, rama, ruta, bloque, método, clase, clase de equivalencia, caso de uso ... lo que decida el equipo).
Por lo tanto, use herramientas para garantizar la cobertura y diseñe sus pruebas para causar los menores costos (a corto y largo plazo ).
No haga que las pruebas sean más caras de lo necesario. Si es más barato probar solo los puntos de entrada públicos, hazlo. Si es más barato probar métodos privados, hazlo.
A medida que tenga más experiencia, será mejor para predecir cuándo vale la pena refactorizar para evitar los costos a largo plazo del mantenimiento de la prueba.
fuente
Si el método es lo suficientemente significativo / complejo, generalmente lo haré "protegido" y lo probaré. Algunos métodos se dejarán privados y se probarán implícitamente como parte de las pruebas unitarias para los métodos públicos / protegidos.
fuente
Veo que muchas personas están en la misma línea de pensamiento: prueba a nivel público. ¿Pero no es eso lo que hace nuestro equipo de control de calidad? Prueban la entrada y la salida esperada. Si como desarrolladores solo probamos los métodos públicos, simplemente estamos rehaciendo el trabajo de QA y no agregamos ningún valor mediante "pruebas unitarias".
fuente
La respuesta a "¿Debo probar métodos privados?" es a veces". Por lo general, deberías probar con la interfaz de tus clases.
Aquí hay un ejemplo:
En
RefactoredThing
ahora tiene 5 pruebas, 2 de las cuales era necesario actualizar para refactorización, pero la funcionalidad de su objeto no ha cambiado realmente. Entonces, digamos que las cosas son más complejas que eso y tiene algún método que define el orden de la salida, como:Esto no debe ser ejecutado por un usuario externo, pero su clase de encapsulación puede ser demasiado pesada para ejecutar tanta lógica una y otra vez. En este caso, tal vez prefiera extraer esto en una clase separada, darle a esa clase una interfaz y probarla.
Y finalmente, digamos que su objeto principal es súper pesado, y el método es bastante pequeño y realmente necesita asegurarse de que la salida sea correcta. Estás pensando: "Tengo que probar este método privado". ¿Puede que tal vez pueda hacer que su objeto sea más ligero al pasar parte del trabajo pesado como parámetro de inicialización? Entonces puedes pasar algo más ligero y probar contra eso.
fuente
No No deberías probar los métodos privados ¿por qué? y, además, el marco burlón popular como Mockito no proporciona soporte para probar métodos privados.
fuente
Un punto principal es
Si probamos para asegurar la corrección de la lógica, y un método privado lleva una lógica, deberíamos probarlo. ¿No es así? Entonces, ¿por qué vamos a saltar eso?
Escribir pruebas basadas en la visibilidad de los métodos es una idea completamente irrelevante.
Por el contrario
Por otro lado, llamar a un método privado fuera de la clase original es un problema principal. Y también hay limitaciones para burlarse de un método privado en algunas herramientas de burla. (Ej: Mockito )
Aunque hay algunas herramientas como Power Mock que lo soporta, es una operación peligrosa. La razón es que necesita hackear la JVM para lograr eso.
Una solución que se puede hacer es (si desea escribir casos de prueba para métodos privados)
Declare esos métodos privados como protegidos . Pero puede no ser conveniente para varias situaciones.
fuente
No se trata solo de métodos o funciones públicas o privadas, se trata de detalles de implementación. Las funciones privadas son solo un aspecto de los detalles de implementación.
La prueba unitaria, después de todo, es un enfoque de prueba de caja blanca. Por ejemplo, quien usa el análisis de cobertura para identificar partes del código que se han descuidado en las pruebas hasta ahora, entra en los detalles de implementación.
A) Sí, debería probar los detalles de implementación:
Piense en una función de clasificación que, por razones de rendimiento, utiliza una implementación privada de BubbleSort si hay hasta 10 elementos, y una implementación privada de un enfoque de clasificación diferente (digamos, montón) si hay más de 10 elementos. La API pública es la de una función de clasificación. Sin embargo, su conjunto de pruebas utiliza mejor el conocimiento de que en realidad se utilizan dos algoritmos de clasificación.
En este ejemplo, seguramente, podría realizar las pruebas en la API pública. Sin embargo, esto requeriría tener una serie de casos de prueba que ejecuten la función de clasificación con más de 10 elementos, de modo que el algoritmo de ordenación esté suficientemente probado. La existencia de tales casos de prueba solo es una indicación de que el conjunto de pruebas está conectado a los detalles de implementación de la función.
Si los detalles de implementación de la función de clasificación cambian, tal vez en la forma en que el límite entre los dos algoritmos de clasificación se cambia o que el montón se reemplaza por mergesort o lo que sea: las pruebas existentes continuarán funcionando. Sin embargo, su valor es cuestionable, y es probable que necesiten ser reelaborados para probar mejor la función de clasificación modificada. En otras palabras, habrá un esfuerzo de mantenimiento a pesar del hecho de que las pruebas se realizaron en la API pública.
B) Cómo probar los detalles de implementación
Una razón por la cual muchas personas argumentan que uno no debería probar las funciones privadas o los detalles de implementación es que es más probable que los detalles de implementación cambien. Esta mayor probabilidad de cambio al menos es una de las razones para ocultar los detalles de implementación detrás de las interfaces.
Ahora, suponga que la implementación detrás de la interfaz contiene partes privadas más grandes para las cuales las pruebas individuales en la interfaz interna podrían ser una opción. Algunas personas argumentan que estas partes no deben probarse cuando son privadas, sino que deben convertirse en algo público. Una vez público, probar el código de la unidad estaría bien.
Esto es interesante: si bien la interfaz era interna, era probable que cambiara, siendo un detalle de implementación. Al tomar la misma interfaz, hacerla pública hace una transformación mágica, es decir, convertirla en una interfaz que es menos probable que cambie. Obviamente hay algún defecto en esta argumentación.
Sin embargo, hay algo de verdad detrás de esto: cuando se prueban los detalles de implementación, en particular el uso de interfaces internas, uno debe esforzarse por usar interfaces que probablemente se mantengan estables. Sin embargo, si es probable que alguna interfaz sea estable no es simplemente decidible en función de si es pública o privada. En los proyectos del mundo en los que he estado trabajando durante algún tiempo, las interfaces públicas también cambian con bastante frecuencia, y muchas interfaces privadas han permanecido intactas durante siglos.
Aún así, es una buena regla general usar la "puerta principal primero" (ver http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Pero tenga en cuenta que se llama "puerta de entrada primero" y no "puerta de entrada solamente".
C) Resumen
Prueba también los detalles de implementación. Prefiere probar en interfaces estables (públicas o privadas). Si los detalles de implementación cambian, también se deben revisar las pruebas en la API pública. Convertir algo privado en público no cambia mágicamente su estabilidad.
fuente
Sí, debe probar métodos privados, siempre que sea posible. ¿Por qué? Para evitar una explosión innecesaria de espacio de estado de casos de prueba que finalmente terminan probando implícitamente las mismas funciones privadas repetidamente en las mismas entradas. Vamos a explicar por qué con un ejemplo.
Considere el siguiente ejemplo ligeramente artificial. Supongamos que queremos exponer públicamente una función que toma 3 enteros y devuelve verdadero si y solo si esos 3 enteros son primos. Podríamos implementarlo así:
Ahora, si tuviéramos que adoptar el enfoque estricto de que solo se deberían probar las funciones públicas, solo se nos permitiría probar
allPrime
y noisPrime
oandAll
.Como probador, podemos estar interesados en cinco posibilidades para cada argumento:
< 0
,= 0
,= 1
,prime > 1
,not prime > 1
. Pero para ser exhaustivos, también tendríamos que ver cómo se combinan todas las combinaciones de los argumentos. Entonces eso es5*5*5
= 125 casos de prueba que necesitaríamos probar a fondo esta función, de acuerdo con nuestras intuiciones.Por otro lado, si se nos permitiera probar las funciones privadas, podríamos cubrir tanto terreno con menos casos de prueba. Necesitaríamos solo 5 casos de prueba
isPrime
para probar al mismo nivel que nuestra intuición anterior. Y según la hipótesis de pequeño alcance propuesta por Daniel Jackson, solo tendríamos que probar laandAll
función hasta una longitud pequeña, por ejemplo, 3 o 4. Lo que sería como máximo 16 pruebas más. Entonces 21 pruebas en total. En lugar de 125. Por supuesto, probablemente querríamos ejecutar algunas pruebas enallPrime
, pero no nos sentiríamos tan obligados a cubrir exhaustivamente las 125 combinaciones de escenarios de entrada que dijimos que nos preocupaban. Solo algunos senderos felices.Un ejemplo artificial, seguro, pero era necesario para una demostración clara. Y el patrón se extiende al software real. Las funciones privadas son generalmente los bloques de construcción de nivel más bajo y, por lo tanto, a menudo se combinan para producir una lógica de nivel más alto. Es decir, en niveles superiores, tenemos más repeticiones de las cosas de nivel inferior debido a las diversas combinaciones.
fuente
isPrime
son verdaderamente independientes, por lo que probar cada combinación a ciegas no tiene ningún propósito. En segundo lugar, marcar una función pura llamadaisPrime
privada viola tantas reglas de diseño que ni siquiera sé por dónde empezar.isPrime
claramente debe ser una función pública. Dicho esto, entiendo lo que estás diciendo, independientemente de este ejemplo extremadamente pobre. Sin embargo, se basa en la premisa de que desearía realizar pruebas de combinación, cuando en sistemas de software reales rara vez es una buena idea.También puede hacer que su método sea privado de paquete, es decir, predeterminado y debería poder probarlo a menos que sea necesario que sea privado.
fuente