Este artículo compara cuatro enfoques diferentes para probar métodos privados en clases Java.
Mi primer uso de JUnit fue construir un kit de prueba de conformidad para la API de ServiceUI . El propósito de un kit de prueba de conformidad es ayudar a garantizar que las implementaciones alternativas de la misma API sean compatibles con la especificación de la API. Debido a que una especificación de API define solo la interfaz pública de la API, no la implementación de la API, una prueba de conformidad solo ejercita la interfaz pública. En otras palabras, una prueba de conformidad es una prueba de «caja negra». Trata la API bajo prueba como una caja negra, cuya interfaz externa se puede ver, pero cuya implementación interna no se puede ver. Por lo tanto, una prueba de conformidad de una API de Java solo necesita acceder a los miembros públicos de los paquetes y clases bajo prueba. No es necesario acceder a miembros a nivel de paquete, protegidos o privados.
Cuando más tarde apliqué JUnit a la tarea de escribir pruebas unitarias reales, en lugar de pruebas de conformidad, me encontré con que quería escribir pruebas de caja blanca, pruebas que emplean el conocimiento de la implementación interna de los paquetes y clases bajo prueba. Mientras que solo quería probar métodos públicos en mis pruebas de conformidad, quería escribir pruebas unitarias para el acceso a paquetes y ocasionalmente métodos privados, así como métodos públicos.
Daniel Steinberg me mostró la técnica común de JUnit de usar árboles de código fuente paralelos, lo que me permitió colocar clases de prueba en el mismo paquete que las clases bajo prueba, pero mantenerlas en un directorio diferente. Esto proporcionó una separación limpia del código de prueba y de producción. Al colocar ambos árboles de fuentes en la ruta de clases, mis clases de prueba podían acceder a métodos y clases a nivel de paquete en el paquete bajo prueba. Sin embargo, esto todavía me dejaba con el problema de probar métodos privados.
Cuando le pregunté a Daniel acerca de probar métodos privados, sugirió suavemente que probara los métodos privados indirectamente probando los métodos de acceso a paquetes y públicos que llaman a los privados. Esta respuesta no me satisfizo del todo, porque en ocasiones realmente sentí la necesidad de probar directamente un método privado. Mi solución inicial fue simplemente hacer acceso a paquetes de métodos privados, lo que me permitió probarlos directamente con JUnit de las clases de prueba en el mismo paquete en el árbol de fuentes paralelo. Esto funcionó bien, pero me hizo sentir un poco sucia de alguna manera. Aunque en general descubrí que pensar en cómo diseñar interfaces para que pudieran probarse fácilmente por unidad me ayudó a diseñar mejores interfaces, en este caso sentí que estaba empeorando un poco el diseño para que fuera comprobable.
Cuando más tarde terminé participando en la creación de lo que Frank Sommers, Matt Gerrans y yo finalmente lanzamos como Artima SuiteRunner, juré que haría las pruebas de métodos privados más fáciles en SuiteRunner que en JUnit. Pero después de investigar los diversos enfoques para probar métodos privados, decidí no hacer nada especial en SuiteRunner para apoyar la prueba de métodos privados. Así que, ya sea que esté utilizando JUnit o SuiteRunner, tiene los mismos cuatro enfoques básicos para probar métodos privados:
- No pruebes métodos privados.
- Dar acceso al paquete de métodos.
- Utilice una clase de prueba anidada.
- Utilice reflexión.
En este artículo, analizaré estos cuatro enfoques para probar métodos privados en Java. Analizaré las ventajas y desventajas de cada uno de ellos e intentaré arrojar algo de luz sobre cuándo tiene sentido utilizar cada enfoque.
Como mencioné en la introducción, escuché por primera vez el consejo de Daniel Steinberg de suprimir mis impulsos ocasionales de probar métodos privados. Pero Daniel no es solo la fuente de este consejo que he encontrado. Parece ser una actitud común en la comunidad Java. Por ejemplo, las preguntas frecuentes de JUnit dicen:
Probar métodos privados puede ser una indicación de que esos métodos deben moverse a otra clase para promover la reutilización.
Charles Miller expresó un punto de vista similar en su blog:
Si tiene un conjunto completo de pruebas para la interfaz expuesta (no privada) de una clase, esas pruebas deben, por su naturaleza, verificar que cualquier método privado dentro de la clase también funcione. Si este no es el caso, o si tiene un método privado tan complejo que necesita ser probado fuera del contexto de sus personas que llaman al público, lo consideraría un olor a código.
Y Dave Thomas y Andy Hunt , en su libro Pragmatic Unit Testing, escriben:
En general, no quieres romper ninguna encapsulación por el bien de las pruebas (o como mamá solía decir, «¡no expongas tus partes privadas!»). La mayoría de las veces, debería ser capaz de probar una clase mediante el ejercicio de sus métodos públicos. Si hay una funcionalidad significativa que está oculta detrás del acceso privado o protegido, eso podría ser una señal de advertencia de que hay otra clase ahí luchando por salir.
Creo en todos estos consejos. La mayoría de las veces, los métodos privados se pueden probar de manera más efectiva a través del enfoque 1, indirectamente probando los métodos públicos, protegidos y de nivel de paquete que los llaman. Pero inevitablemente, algunas personas en algunas situaciones sentirán que probar directamente un método privado es lo correcto.
En mi caso, tiendo a crear muchos métodos de utilidad privada. Estos métodos de utilidad a menudo no hacen nada con los datos de instancia, solo operan en los parámetros pasados y devuelven un resultado. Creo estos métodos para que el método de llamada sea más fácil de entender. Es una forma de gestionar la complejidad de la implementación de la clase. Ahora, si extraigo el método privado de un método que ya funciona y tiene una buena cobertura de prueba unitaria, entonces las pruebas unitarias existentes probablemente serán suficientes. No necesito escribir más pruebas unitarias solo para el método privado. Pero si quiero escribir el método privado antes de su método de llamada, y quiero escribir las pruebas unitarias antes de escribir el método privado, he vuelto a querer probar directamente el método privado. En el caso de los métodos de utilidad privada, no siento que mi impulso de probar directamente los métodos sea, como lo expresó el FAQ de JUnit, «una indicación de que esos métodos deberían trasladarse a otra clase para promover la reutilización.»En realidad, estos métodos solo se necesitan en la clase en la que residen, y de hecho a menudo solo se llaman por otro método.
Otra razón por la que a veces siento la necesidad de probar métodos privados directamente es que tiendo a pensar que las pruebas unitarias me ayudan a lograr un sistema robusto al construir ese sistema a partir de piezas robustas. Cada parte es una » unidad «para la que puedo escribir «pruebas unitarias».»Las pruebas unitarias me ayudan a garantizar que cada unidad funcione correctamente, lo que a su vez me ayuda a construir un sistema que funcione correctamente en su conjunto. La unidad principal que creo en términos de cuándo programar en Java es la clase. Construyo sistemas a partir de clases, y las pruebas unitarias me dan la confianza de que mis clases son robustas. Pero hasta cierto punto también siento lo mismo sobre los métodos privados con los que compongo paquetes: acceso, métodos protegidos y métodos públicos. Estos métodos privados son unidades que se pueden probar individualmente. Estas pruebas unitarias me dan la confianza de que los métodos privados funcionan correctamente, lo que me ayuda a crear métodos de acceso a paquetes, protegidos y públicos robustos.
Como mencioné en la introducción, dar acceso a paquetes de métodos fue mi primer enfoque para probar métodos privados con JUnit. Este enfoque en realidad funciona bien, pero viene con un ligero costo. Cuando veo un especificador de acceso privado en un método, me dice algo que me gusta saber: que esto es parte de la implementación de la clase. Sé que puedo ignorar el método si solo estoy tratando de usar la clase de otra clase en el paquete. Podría averiguar esto sobre un método de acceso a paquetes mirando más de cerca el nombre, la documentación y el código del método, pero la palabra privado comunica esto de manera mucho más eficiente. Además, el principal problema que tengo con este enfoque es filosófico. Aunque no me importa «romper la encapsulación por el bien de las pruebas», como lo dirían Dave y Andy, simplemente no me siento bien al romper la encapsulación de una manera que cambie la API a nivel de paquete. En otras palabras, aunque me entusiasma probar métodos de clases no públicos, es decir, crear pruebas unitarias de «caja blanca», prefiero que la API de las clases bajo prueba, incluida la API a nivel de paquete, no se cambie para facilitar esas pruebas.
Enfoque 3: Utilice una Clase de prueba anidada
Un tercer enfoque para probar métodos privados es anidar una clase de prueba estática dentro de la clase de producción que se está probando. Dado que una clase anidada tiene acceso a los miembros privados de su clase que la encierra, podría invocar los métodos privados directamente. La clase estática en sí misma podría ser acceso a paquetes, lo que le permitiría cargarse como parte de la prueba de caja blanca.
La desventaja de este enfoque es que si no desea que la clase de prueba anidada sea accesible en su archivo JAR de implementación, debe hacer un poco de trabajo adicional para extraerla. Además, es posible que a algunas personas no les guste que el código de prueba se mezcle en el mismo archivo que el código de producción, aunque otros pueden preferir ese enfoque.
Enfoque 4: Use Reflexión
El cuarto enfoque para probar métodos privados me fue sugerido por Vladimir R. Bossicard, quien escribió Complementos de JUnit . Un día durante el almuerzo, Vladimir me explicó que la API java.lang.reflect
incluía métodos que permitían que el código del cliente eludiera el mecanismo de protección de acceso de la máquina virtual Java. También me dijo que su proyecto JUnit Addons incluía una clase, junitx.util.PrivateAccessor
, para ayudar a usar la API de reflexión para este propósito: escribir pruebas unitarias que manipulen a los miembros privados de las clases bajo prueba. Las preguntas frecuentes de JUnit apuntan a una clase similar, llamada PrivilegedAccessor
, escrita por Charlie Hubbard y Prashant Dhotke.
Una ventaja de usar el enfoque de reflexión para probar métodos privados es que proporciona una separación limpia del código de prueba y el código de producción. No es necesario que los ensayos estén anidados dentro de la clase objeto de ensayo, como en la aproximación 3. Más bien, se pueden colocar junto a las otras pruebas que ejercen los métodos públicos y de nivel de paquete de la clase. Además, no es necesario alterar la API de la clase en prueba. A diferencia del enfoque 2, los métodos privados pueden permanecer privados. A diferencia del enfoque 3, no es necesario agregar ninguna clase anidada adicional a nivel de acceso al paquete. La principal desventaja de este enfoque es que el código de prueba es mucho más detallado porque utiliza la API de reflexión. Además, los IDE de refactorización como Eclipse e IntelliJ generalmente no son tan expertos en cambiar los nombres de los métodos donde se les conoce como String
s pasados a los métodos de la API de reflexión. Por lo tanto, si cambia el nombre del método privado con su IDE de refactorización, es posible que aún tenga que hacer algunos cambios a mano en el código de prueba.
Un ejemplo
Para dar un ejemplo de un método privado que, en mi opinión, merece pruebas unitarias directas, extraje algunas funcionalidades del métodomain
de claseorg.suiterunner.Runner
.Runner.main
analiza los argumentos de la línea de comandos y ejecuta un conjunto de pruebas, activando opcionalmente la interfaz gráfica de usuario. El método que extraje,parseArgsIntoLists
, hace parte del trabajo de analizar los argumentos de la línea de comandos en la aplicación SuiteRunner. Ahora, para probar el método público que llama a este método privado, necesitaría probarmain
. Lo principal, por supuesto, es toda la aplicación, lo que hace que el método sea bastante difícil de probar. De hecho, no tengo ninguna prueba paramain
.
En este punto, es posible que se pregunte, si estaba escribiendo pruebas primero en el estilo de desarrollo basado en pruebas, ¿cómo terminé escribiendo código de análisis que no tenía pruebas unitarias? La razón principal es que mi prueba de infección ha llegado en etapas. De hecho, cogí una gripe de prueba unitaria mucho antes de haber oído hablar de JUnit o read Test Infected . Cuando estaba creando aplicaciones de Windows en C++, por ejemplo, escribía un poco de código para probar un método recién implementado, luego ejecutaba ese código y lo veía ejecutarse al pasar por el método bajo prueba con el depurador. Este tipo de pruebas unitarias me ayudó a lograr robustez, pero las pruebas en sí no comprobaron el comportamiento adecuado. Yo mismo comprobé el comportamiento adecuado observando a través del depurador. Las pruebas no estaban automatizadas y, por lo tanto, no las guardé para que pudieran ejecutarse de nuevo más tarde. Cuando leí Test Infected, inmediatamente vi el valor de automatizar las pruebas y mantenerlas como una especie de prueba de regresión después de la refactorización, pero durante mucho tiempo no tenía sentido escribir las pruebas primero. Quería escribir las pruebas después de implementar la funcionalidad, porque fue entonces cuando ejecuté las pruebas con el depurador. Una razón secundaria por la que no escribí las pruebas primero mientras desarrollaba gran parte de SuiteRunner es que quería escribir las pruebas de SuiteRunner con SuiteRunner en sí, en un esfuerzo por comer mi propia comida para perros. Hasta que se estableció la API básica de SuiteRunner, no tenía el kit de herramientas de prueba que quería usar para escribir las pruebas.
Desde entonces, sin embargo, el virus de pruebas ha tomado un control más fuerte sobre mí, y ahora prefiero escribir pruebas unitarias primero la mayor parte del tiempo. Prefiero escribir pruebas primero, no tanto porque encuentro que termino con diseños más limpios, que generalmente se promociona como el principal beneficio del desarrollo basado en pruebas. Más bien, prefiero escribir pruebas primero porque encuentro que a menudo si me meto en el código bajo presión, con la intención de escribir la prueba más tarde, la prueba en realidad nunca se escribe. El SuiteRunner en sí tiene muy pocas pruebas unitarias en este momento por esa misma razón. Aquí está el método parseArgsIntoLists
:
private static void parseArgsIntoLists(String args, List runpathList, List reportersList, List suitesList) { if (args == null || runpathList == null || reportersList == null || suitesList == null) { throw new NullPointerException(); } for (int i = 0; i < args.length; i++) { if (args.startsWith("-p")) { runpathList.add(args); runpathList.add(args); ++i; } else if (args.startsWith("-g")) { reportersList.add(args); } else if (args.startsWith("-o")) { reportersList.add(args); } else if (args.startsWith("-e")) { reportersList.add(args); } else if (args.startsWith("-f")) { reportersList.add(args); reportersList.add(args); ++i; } else if (args.startsWith("-r")) { reportersList.add(args); reportersList.add(args); ++i; } else if (args.startsWith("-s")) { suitesList.add(args); do { ++i; suitesList.add(args); } while (i + 1 < args.length); } else { throw new IllegalArgumentException("Unrecognized argument: " + args); } } }
La línea de comandos de SuiteRunner contiene tres tipos de información que utiliza SuiteRunner para ejecutar pruebas: ruta de ejecución, reporteros y conjuntos. El método parseArgsIntoLists
simplemente pasa por los argumentos pasados como una matriz de String
s, y coloca cada argumento en una de las listas, runpathList
, reportersList
y suitesList
.
Antes de escribir una prueba para este método privado, me gustaría preguntar si mi deseo de escribir esta prueba unitaria representa un olor a código, como lo puso Charles Miller en su blog. ¿Indica que parseArgsIntoLists
se debe mover a otra clase para promover la reutilización, como sugiere el FAQ de JUnit? ¿Dave y Andy dirían que es una señal de advertencia de que hay otra clase luchando por salir? Bueno, tal vez. Podría crear concientemente una clase ArgumentsParser
que solo contenga unos pocos métodos estáticos que realicen el trabajo de análisis. Tanto la clase ArgumentsParser
como los métodos que contiene podrían ser acceso a paquetes, lo que los haría fáciles de probar. Pero eso no me parece bien. Estos métodos solo son llamados por Runner.main
. Claramente se sienten como métodos privados para mí. La única razón por la que los estaría moviendo a una clase ArgumentsParser
es para poder probarlos. De hecho, estaría utilizando el enfoque número 2: hacer que los métodos privados tengan acceso al paquete.
En su lugar, para este ejemplo decidí tomar el enfoque número 4 y usar reflexión. Miré tanto el junitx.utils.PrivateAccessor
de Vladimir Bossicard como el PrivilegedAccessor
de Charlie Hubbard y Prashant Dhotke, pero decidí que ninguno de ellos me ayudó como yo quería. Por un lado, ambas clases tienen la capacidad de probar campos para asegurarse de que estén configurados correctamente. Hasta ahora, nunca he sentido ninguna necesidad de acceder directamente a los campos privados de las pruebas unitarias. Solo quiero poder probar métodos de servicios privados. El principal problema que tuve con estas dos clases, sin embargo, es cómo lidiaban con las excepciones que se pueden lanzar al tratar de invocar el método privado a través de la reflexión. Cada clase tiene uno o más métodos cuyo trabajo es invocar un método con reflexión. Los dos métodos invokeMethod
de PrivilegedAccessor
devuelven cualquier excepción a su autor de llamada, incluidas tres excepciones marcadas declaradas en la cláusula throws: NoSuchMethodException
, IllegalAccessException
y InvocationTargetException
. Por el contrario, los dos métodos invoke
de PrivateAccessor
capturan InvocationTargetException
, y extraen y lanzan la excepción de destino, la excepción real lanzada por el método invocado. A continuación, captura cualquier otra excepción y lanza NoSuchMethodException
. No me gustó que el llamante de PrivilegedAccessor.invokeMethod
siempre tuviera que manejar las tres excepciones verificadas, porque pensé que la forma general de manejar cualquier excepción sería dejar que la prueba fallara. También me preocupaba que PrivateAccessor.invoke
estuviera desechando información de seguimiento de pila potencialmente útil en su política de manejo de excepciones. Lo que realmente quería era un método que intentara invocar un método privado con reflexión, que envolviera cualquier excepción lanzada además de InvocationTargetException
en un TestFailedException
sin marcar. La mayoría de las veces, esta excepción causaría que la prueba fallara. En las pruebas que esperaban que se lanzara una excepción, la excepción contenida en InvocationTargetException
podría extraerse y probarse para verificar su corrección.
Por lo tanto, escribí invokeStaticMethod
. La llamada setAccessible(true)
es lo que permite invocar el método privado desde fuera de la clase. Una implementación invokeStaticMethod
correspondiente para usar con JUnit arrojaría AssertionFailedError
en lugar de TestFailedException
. Aquí está el código:
private static void invokeStaticMethod(Class targetClass, String methodName, Class argClasses, Object argObjects) throws InvocationTargetException { try { Method method = targetClass.getDeclaredMethod(methodName, argClasses); method.setAccessible(true); method.invoke(null, argObjects); } catch (NoSuchMethodException e) { // Should happen only rarely, because most times the // specified method should exist. If it does happen, just let // the test fail so the programmer can fix the problem. throw new TestFailedException(e); } catch (SecurityException e) { // Should happen only rarely, because the setAccessible(true) // should be allowed in when running unit tests. If it does // happen, just let the test fail so the programmer can fix // the problem. throw new TestFailedException(e); } catch (IllegalAccessException e) { // Should never happen, because setting accessible flag to // true. If setting accessible fails, should throw a security // exception at that point and never get to the invoke. But // just in case, wrap it in a TestFailedException and let a // human figure it out. throw new TestFailedException(e); } catch (IllegalArgumentException e) { // Should happen only rarely, because usually the right // number and types of arguments will be passed. If it does // happen, just let the test fail so the programmer can fix // the problem. throw new TestFailedException(e); } }
A continuación, creé un método de conveniencia que invoca el método privado particular que quería probar:
private static void invokeParseArgsIntoLists(String args, List runpathList, List reportersList, List suitesList) throws InvocationTargetException { // Purposely pass null values to the method, to make sure it throws // NullPointerException Class argClasses = {String.class, List.class, List.class, List.class }; Object argObjects = {args, runpathList, reportersList, suitesList }; invokeStaticMethod(Runner.class, "parseArgsIntoLists", argClasses, argObjects); }
Por fin, pude escribir pruebas contra el método privado sin demasiado exceso de desorden, como este:
public void testParseArgsIntoLists() throws InvocationTargetException { String args = new String; List runpathList = new ArrayList(); List reportersList = new ArrayList(); List suitesList = new ArrayList(); try { invokeParseArgsIntoLists(null, runpathList, reportersList, suitesList); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } try { invokeParseArgsIntoLists(args, null, reportersList, suitesList); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } try { invokeParseArgsIntoLists(args, runpathList, null, suitesList); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } try { invokeParseArgsIntoLists(args, runpathList, reportersList, null); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } args = new String; args = "-p"; args = "\"mydir\""; args = "-g"; args = "-f"; args = "test.out"; args = "-s"; args = "MySuite"; runpathList.clear(); reportersList.clear(); suitesList.clear(); invokeParseArgsIntoLists(args, runpathList, reportersList, suitesList); verify(runpathList.size() == 2); verify(runpathList.get(0).equals(args)); verify(runpathList.get(1).equals(args)); verify(reportersList.size() == 3); verify(reportersList.get(0).equals(args)); verify(reportersList.get(1).equals(args)); verify(reportersList.get(2).equals(args)); verify(suitesList.size() == 2); verify(suitesList.get(0).equals(args)); verify(suitesList.get(1).equals(args)); args = new String; args = "-p"; args = "\"mydir\""; args = "-e"; args = "-o"; args = "-r"; args = "MyCustomReporter"; args = "-s"; args = "MySuite"; args = "MyOtherSuite"; runpathList.clear(); reportersList.clear(); suitesList.clear(); invokeParseArgsIntoLists(args, runpathList, reportersList, suitesList); verify(runpathList.size() == 2); verify(runpathList.get(0).equals(args)); verify(runpathList.get(1).equals(args)); verify(reportersList.size() == 4); verify(reportersList.get(0).equals(args)); verify(reportersList.get(1).equals(args)); verify(reportersList.get(2).equals(args)); verify(reportersList.get(3).equals(args)); verify(suitesList.size() == 3); verify(suitesList.get(0).equals(args)); verify(suitesList.get(1).equals(args)); verify(suitesList.get(2).equals(args)); args = new String; args = "-p"; args = "\"serviceuitest-1.1beta4.jar myjini http://myhost:9998/myfile.jar\""; args = "-g"; args = "-s"; args = "MySuite"; args = "MySecondSuite"; args = "MyThirdSuite"; args = "MyFourthSuite"; args = "MyFifthSuite"; args = "MySixthSuite"; runpathList.clear(); reportersList.clear(); suitesList.clear(); invokeParseArgsIntoLists(args, runpathList, reportersList, suitesList); verify(runpathList.size() == 2); verify(runpathList.get(0).equals(args)); verify(runpathList.get(1).equals(args)); verify(reportersList.size() == 1); verify(reportersList.get(0).equals(args)); verify(suitesList.size() == 7); verify(suitesList.get(0).equals(args)); verify(suitesList.get(1).equals(args)); verify(suitesList.get(2).equals(args)); verify(suitesList.get(3).equals(args)); verify(suitesList.get(4).equals(args)); verify(suitesList.get(5).equals(args)); verify(suitesList.get(6).equals(args)); }
Conclusión
El enfoque 1, probar métodos privados indirectamente probando los métodos públicos, protegidos y a nivel de paquete que los llaman, a menudo será el mejor enfoque. En los casos en los que realmente desea probar métodos privados directamente, el uso de la reflexión para probar métodos privados, aunque bastante engorroso, proporciona la separación más limpia del código de prueba del código de producción y el menor impacto en el código de producción. Sin embargo, si no le importa hacer esos métodos privados en particular que desea probar el acceso a los paquetes, podría usar el enfoque 2. O si no le importa colocar una clase de prueba anidada dentro de su clase de producción bajo prueba, el enfoque 3 al menos le permitiría mantener los métodos privados privados.
no Hay una respuesta perfecta. Pero si adopta el enfoque 4, en última instancia, terminará con un puñado de métodos como invokeStaticMethod
que puede reutilizar. Una vez que escriba un método conveniente, como invokeParseArgsIntoLists
, para un método privado, puede escribir pruebas contra el método privado sin mucha dificultad.
Recursos
1. La API de ServiceUI define una forma estándar de adjuntar interfaces de usuario a los servicios Jini:
http://www.artima.com/jini/serviceui/index.html
2. Daniel Steinberg es actualmente el Editor en Jefe de Java.NET:
http://www.java.net/
3. Artima SuiteRunner es un kit de herramientas de prueba de código abierto gratuito y JUnit runner:
http://www.artima.com/suiterunner/index.html
4.Preguntas frecuentes de JUnit sobre la prueba de métodos privados:
http://junit.sourceforge.net/doc/faq/faq.htm#tests_10
5. Probando Métodos Privados (No Lo hagas), una publicación de weblog de Charles Miller:
http://fishbowl.pastiche.org/2003/03/28/testing_private_methods_dont_do_it
6. Andy Hunt y Dave Thomas son los autores de Pragmatic Unit Testing, que está disponible en la tienda Pragmatic.
7. JUnit Addons es una colección de clases de ayuda para JUnit creadas por Vladimar R. Bossicard:
http://sourceforge.net/projects/junit-addons
PrivateAccessor
es la clase de JUnit Addons que facilita la prueba de miembros privados:
http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html
9.Clase PrivilegedAccessor, que puede usar para acceder a miembros privados:
http://groups.yahoo.com/group/junit/files/src/PrivilegedAccessor.java
10. Test Driven Development by Example, de Kent Beck, describe la primera técnica de prueba:
http://www.amazon.com/exec/obidos/ASIN/0321146530/
11. Test Infected, de Kent Beck y Erich Gamma, presentó a JUnit al mundo:
http://junit.sourceforge.net/doc/testinfected/testing.htm
Métodos Privados de Pruebas unitarias, una publicación en weblog sobre NUnit por Ted Graham:
http://weblogs.asp.net/tgraham/archive/2003/12/31/46984.aspx
Subvertir la Protección de Acceso de Java para Pruebas Unitarias, un O’Reilly OnJava.com artículo de Ross Burton:
http://www.onjava.com/pub/a/onjava/2003/11/12/reflection.html
La clase RunnerSuite
, de la que se tomaron los fragmentos de código de este artículo, aparece en su totalidad aquí:
http://www.artima.com/suiterunner/privateExample.html
Por Qué Refactorizamos JUnit
http://www.artima.com/suiterunner/why.html
Tutorial de Artima SuiteRunner, Conformidad de construcción y Pruebas Unitarias con Artima SuiteRunner:
http://www.artima.com/suiterunner/tutorial.html
Primeros pasos con Artima SuiteRunner, Cómo Ejecutar el Ejemplo Sencillo Incluido en la Distribución:
http://www.artima.com/suiterunner/start.html
Ejecución de pruebas JUnit con Artima SuiteRunner, cómo usar Artima SuiteRunner como JUnit runner para ejecutar sus conjuntos de pruebas JUnit existentes:
http://www.artima.com/suiterunner/junit.html
Página de inicio de Artima SuiteRunner:
http://www.artima.com/suiterunner/index.html
Página de descarga de Artima SuiteRunner (Debe iniciar sesión Artima.com para descargar la versión):
http://www.artima.com/suiterunner/download.jsp
El Foro de SuiteRunner:
http://www.artima.com/forums/forum.jsp?forum=61
¡Responde!
¿Tiene una opinión? Los lectores ya han publicado 26 comentarios sobre este artículo. ¿Por qué no agregas el tuyo?
Sobre el autor
Bill Venners es presidente de Artima Software, Inc. y redactor jefe de Artima.com Es autor del libro Inside the Java Virtual Machine, un estudio orientado a programadores de la arquitectura y el funcionamiento interno de la plataforma Java. Sus populares columnas en la revista JavaWorld cubrían el funcionamiento interno de Java, el diseño orientado a objetos y el Jini. Bill ha estado activo en la comunidad Jini desde su creación. Dirigió el proyecto ServiceUI de la Comunidad Jini que produjo la API de ServiceUI. El ServiceUI se convirtió en la forma estándar de facto de asociar interfaces de usuario a los servicios Jini, y fue el primer estándar de la comunidad Jini aprobado a través del Proceso de Decisión de Jini. Bill también sirve como miembro electo del Comité de Supervisión Técnica Inicial (TOC) de la Comunidad Jini, y en este papel ayudó a definir el proceso de gobernanza para la comunidad. Actualmente dedica la mayor parte de su energía a la construcción Artima.com en un recurso cada vez más útil para los desarrolladores.