una ser que pueden publicas protegidas programacion privadas poo operadores modificadores modificador miembros los delimitadores clases clase acceso accedidos c# java unit-testing

c# - ser - que es private en programacion



Haciendo público un método privado para probarlo en una unidad... ¿buena idea? (30)

Nota del Moderador: Ya hay 39 respuestas publicadas aquí (algunas han sido eliminadas). Antes de publicar su respuesta, considere si puede agregar algo significativo a la discusión o no. Es más que probable que simplemente repita lo que alguien más ya ha dicho.

De vez en cuando me veo en la necesidad de hacer un método privado en una clase pública solo para escribir algunas pruebas unitarias para ello.

Por lo general, esto se debe a que el método contiene lógica compartida entre otros métodos en la clase y es más fácil probar la lógica por sí mismo, o podría ser posible otra razón, si deseo probar la lógica utilizada en hilos sincrónicos sin tener que preocuparme por problemas .

¿Se encuentran otras personas haciendo esto, porque realmente no me gusta hacerlo? Personalmente, creo que los bonos superan los problemas de hacer público un método que realmente no proporciona ningún servicio fuera de la clase ...

ACTUALIZAR

Gracias por las respuestas de todos, parece haber despertado el interés de la gente. Creo que el consenso general es que las pruebas deben realizarse a través de la API pública ya que esta es la única forma en que se usará una clase, y estoy de acuerdo con esto. El par de casos que mencioné anteriormente donde haría esto arriba eran casos poco comunes y pensé que los beneficios de hacerlo valían la pena.

Sin embargo, puedo ver que todo el mundo señala que en realidad nunca debería suceder. Y cuando pienso en ello un poco más, creo que cambiar tu código para adaptar las pruebas es una mala idea. Después de todo, supongo que probar es una herramienta de soporte de alguna manera y cambiar un sistema para "admitir una herramienta de soporte" es evidente. mala práctica.


Nota:
Esta respuesta se publicó originalmente para la pregunta ¿Es la prueba unitaria sola alguna vez una buena razón para exponer las variables de instancia privadas a través de getters? que se fusionó con este, por lo que puede ser un poco específico para el caso de uso presentado allí.

Como afirmación general, generalmente soy todo para refacturar el código de "producción" para que sea más fácil de probar. Sin embargo, no creo que sea una buena decisión aquí. Una buena prueba de unidad (generalmente) no debería preocuparse por los detalles de implementación de la clase, solo acerca de su comportamiento visible. En lugar de exponer las pilas internas a la prueba, puede probar que la clase devuelve las páginas en el orden en que espera después de llamar a first() o last() .

Por ejemplo, considere este pseudo-código:

public class NavigationTest { private Navigation nav; @Before public void setUp() { // Set up nav so the order is page1->page2->page3 and // we''ve moved back to page2 nav = ...; } @Test public void testFirst() { nav.first(); assertEquals("page1", nav.getPage()); nav.next(); assertEquals("page2", nav.getPage()); nav.next(); assertEquals("page3", nav.getPage()); } @Test public void testLast() { nav.last(); assertEquals("page3", nav.getPage()); nav.previous(); assertEquals("page2", nav.getPage()); nav.previous(); assertEquals("page1", nav.getPage()); } }


¿Por qué no dividir el algoritmo de administración de la pila en una clase de utilidad? La clase de utilidad puede administrar las pilas y proporcionar acceso público. Sus pruebas unitarias pueden enfocarse en detalles de implementación. Las pruebas profundas para las clases algorítmicamente difíciles son muy útiles para arrugar las cajas y garantizar la cobertura.

Entonces su clase actual puede delegar limpiamente a la clase de utilidad sin exponer ningún detalle de implementación. Sus pruebas se relacionarán con el requisito de paginación que otros recomendaron.


¿Qué hay de hacer que el paquete sea privado? Entonces su código de prueba puede verlo (y otras clases en su paquete también), pero aún está oculto para sus usuarios.

Pero realmente, no deberías probar métodos privados. Esos son detalles de implementación, y no parte del contrato. Todo lo que hagan debe estar cubierto llamando a los métodos públicos (si tienen un código que los métodos públicos no ejercen, entonces eso debería ir). Si el código privado es demasiado complejo, la clase probablemente está haciendo demasiadas cosas y no necesita refactorización.

Hacer público un método es un gran compromiso. Una vez que lo hagas, las personas podrán usarlo y ya no podrás cambiarlo más.


A menudo agregaré un método llamado algo así como validate , verify , check , etc., a una clase para que pueda ser llamado para probar el estado interno de un objeto.

A veces, este método está envuelto en un bloque ifdef (lo escribo principalmente en C ++) para que no se compile para el lanzamiento. Pero a menudo es útil en la versión para proporcionar métodos de validación que recorren los árboles de objetos del programa comprobando cosas.


Algunas buenas respuestas Una cosa que no vi mencionar es que con desarrollo basado en pruebas (TDD), se crean métodos privados durante la fase de refactorización (mire el Método de Extracción para un ejemplo de un patrón de refactorización) y, por lo tanto, ya debe tener la cobertura de prueba necesaria. . Si se hace correctamente (y por supuesto, obtendrá una mezcla de opiniones cuando se trata de corrección), no debería tener que preocuparse de tener que hacer público un método privado solo para que pueda probarlo.


Como han dicho otros, es un tanto sospechoso estar probando métodos privados en absoluto; pruebe la interfaz pública, no los detalles de la implementación privada.

Dicho esto, la técnica que uso cuando deseo probar la unidad de algo que es privado en C # es rebajar la protección de accesibilidad de privada a interna, y luego marcar el ensamble de prueba de la unidad como un ensamblaje amigo usando InternalsVisibleTo . El conjunto de prueba de la unidad podrá tratar las partes internas como públicas, pero no tendrá que preocuparse por agregarlas accidentalmente a su área de superficie pública.


Diría que es una mala idea porque no estoy seguro de si obtiene algún beneficio y problemas potenciales en el futuro. Si está cambiando el contrato de una llamada, solo para probar un método privado, no está probando la clase en cómo se usaría, sino creando un escenario artificial que nunca tuvo la intención de que suceda.

Además, al declarar el método como público, ¿qué significa que dentro de seis meses (después de olvidar que la única razón para hacer público un método es para probar) que usted (o si ha entregado el proyecto) alguien completamente diferente ganó? No lo use, lo que puede generar consecuencias potencialmente no deseadas y / o una pesadilla de mantenimiento.


En Java, también existe la opción de hacer que el paquete sea privado (es decir, dejar el modificador de visibilidad). Si las pruebas de su unidad están en el mismo paquete que la clase que se está probando, debería poder ver estos métodos, y es un poco más seguro que hacer que el método sea completamente público.


En realidad, hay situaciones en las que deberías hacer esto (por ejemplo, cuando estás implementando algoritmos complejos). Solo hazlo en paquete privado y esto será suficiente. Pero en la mayoría de los casos, probablemente tenga clases demasiado complejas que requieren descomponer la lógica en otras clases.


En tu actualización, dices que es bueno probar utilizando la API pública. En realidad, hay dos escuelas aquí.

  1. Prueba de caja negra

    La escuela Black Box dice que la clase debe considerarse como una caja negra para que nadie pueda ver la implementación dentro de ella. La única manera de probar esto es a través de la API pública, tal como la usará el usuario de la clase.

  2. pruebas de caja blanca.

    La escuela de caja blanca piensa que es natural usar el conocimiento sobre la implementación de la clase y luego probar la clase para saber que funciona como debería.

Realmente no puedo tomar partido en la discusión. Solo pensé que sería interesante saber que hay dos maneras distintas de probar una clase (o una biblioteca o lo que sea).


Generalmente mantengo las clases de prueba en el mismo proyecto / ensamblaje que las clases bajo prueba.
De esta forma solo necesito visibilidad internal para hacer que las funciones / clases sean comprobables.

Esto de alguna manera complica su proceso de construcción, que necesita filtrar las clases de prueba. Lo logro al nombrar todas mis clases de prueba TestedClassTest y usar una expresión regular para filtrar esas clases.

Esto, por supuesto, solo se aplica a la parte C # / .NET de su pregunta


Guava tiene una anotación @VisibleForTesting para marcar los métodos que tienen un alcance ampliado (paquete o público) que de otro modo. Uso una anotación @Private para la misma cosa.

Si bien la API pública debe probarse, a veces es conveniente y sensato obtener material que normalmente no sería público.

Cuando:

  • una clase se hace significativamente menos legible, en su totalidad, dividiéndola en múltiples clases,
  • solo para que sea más comprobable,
  • y proporcionar algún acceso de prueba a las entrañas haría el truco

parece que la religión está superando a la ingeniería.


Los métodos privados generalmente se usan como métodos "auxiliares". Por lo tanto, solo devuelven valores básicos y nunca operan en instancias específicas de objetos.

Tienes un par de opciones si quieres probarlas.

  • Usa la reflexión
  • Proporcione acceso al paquete de métodos

Alternativamente, puede crear una nueva clase con el método auxiliar como método público si es un candidato suficientemente bueno para una nueva clase.

Hay un muy buen artículo aquí sobre esto.


Los métodos privados que desea probar de forma aislada son una indicación de que hay otro "concepto" enterrado en su clase. Extraiga ese "concepto" a su propia clase y pruébelo como una "unidad" separada.

Eche un vistazo a este video para obtener una perspectiva realmente interesante del tema.


Muchas respuestas sugieren solo probar la interfaz pública, pero en mi humilde opinión esto no es realista: si un método hace algo que requiere 5 pasos, querrá probar esos cinco pasos por separado, no todos juntos. Esto requiere probar los cinco métodos, que ( que no sean para la prueba) de otro modo podrían ser private .

La forma habitual de probar métodos "privados" es dar a cada clase su propia interfaz y hacer public métodos "privados", pero no incluirlos en la interfaz. De esta forma, aún pueden ser probados, pero no inflan la interfaz.

Sí, esto dará lugar a la hinchazón de archivos y clases.

Sí, esto hace que los especificadores public y private redundantes.

Sí, esto es un dolor en el culo.

Desafortunadamente, este es uno de los muchos sacrificios que hacemos para que el código sea comprobable . Quizás un lenguaje futuro (o incluso una versión futura de C # / Java) tendrá características para hacer más conveniente la capacidad de prueba de clase y módulo; pero mientras tanto, tenemos que saltar a través de estos aros.

Hay quienes argumentan que cada uno de esos pasos debe ser de su propia clase , pero no estoy de acuerdo: si todos comparten el estado, no hay ninguna razón para crear cinco clases separadas en las que cinco métodos funcionarían. Peor aún, esto da como resultado la hinchazón de archivos y clases. Además , infecta la API pública de su módulo; todas esas clases deben ser necesariamente public si desea probarlas desde otro módulo (o bien, o incluir el código de prueba en el mismo módulo, lo que significa enviar el código de prueba con su producto). )


No, porque hay mejores formas de desollar a ese gato.

Algunos arneses de prueba de unidades se basan en macros en la definición de clase que se expande automágicamente para crear ganchos cuando se construye en modo de prueba. Estilo muy C, pero funciona.

Una expresión idiomática de OO más fácil es hacer que cualquier cosa que desee probar sea "protegida" en lugar de "privada". El arnés de prueba hereda de la clase bajo prueba, y luego puede acceder a todos los miembros protegidos.

O elige la opción "amigo". Personalmente esta es la característica de C ++ que menos me gusta porque rompe las reglas de encapsulación, pero resulta necesario para la forma en que C ++ implementa algunas características, así que hola.

De todos modos, si está realizando pruebas unitarias, es probable que necesite inyectar valores en esos miembros. Los mensajes de texto en la caja blanca son perfectamente válidos. Y eso realmente rompería tu encapsulación.


Nunca jamás dejes que tus pruebas dicten tu código. No estoy hablando de TDD u otros DD, quiero decir, exactamente lo que preguntas. ¿Su aplicación necesita que esos métodos sean públicos? Si lo hace, pruébelos. Si no es así, entonces no los haga públicos solo para las pruebas. Lo mismo con las variables y otros. Deje que las necesidades de su aplicación dicten el código y deje que sus pruebas prueben que se cumple la necesidad. (De nuevo, no me refiero a probar primero o no me refiero a cambiar una estructura de clases para cumplir un objetivo de prueba).

En su lugar, debe "probar más alto". Pruebe el método que llama al método privado. Pero sus pruebas deberían probar sus necesidades de aplicaciones y no sus "decisiones de implementación".

Por ejemplo (bod pseudocódigo aquí);

public int books(int a) { return add(a, 2); } private int add(int a, int b) { return a+b; }

No hay ninguna razón para probar "agregar"; puede probar "libros" en su lugar.

Nunca permita que sus pruebas tomen decisiones de diseño de códigos para usted. Pruebe que obtenga el resultado esperado, no cómo obtiene ese resultado.


OMI, debe escribir sus pruebas sin hacer suposiciones profundas sobre cómo se implementó su clase en el interior. Es probable que desee refactorizarlo más tarde utilizando otro modelo interno, pero aún haciendo las mismas garantías que ofrece la implementación anterior.

Teniendo esto en cuenta, le sugiero que se concentre en las pruebas de que su contrato aún se mantiene, sin importar la implementación interna que su clase tenga actualmente. Prueba basada en propiedades de sus API públicas.


Personalmente, preferiría probar en una sola unidad usando la API pública y ciertamente nunca haré público el método privado solo para que sea fácil de probar.

Si realmente quieres probar el método privado de forma aislada, en Java puedes usar Easymock / Powermock para permitirte hacer esto.

Tienes que ser pragmático al respecto y también debes ser consciente de las razones por las que las cosas son difíciles de probar.

'' Escuche las pruebas '': si es difícil de probar, ¿eso le dice algo sobre su diseño? ¿Podría refactorizarse a dónde una prueba para este método sería trivial y fácilmente cubierta mediante pruebas a través de la API pública?

Esto es lo que Michael Feathers tiene que decir en '' Working Effectively With Legacy Code ''

"Muchas personas pasan mucho tiempo tratando de encontrar la forma de evitar este problema ... la verdadera respuesta es que si tienes la necesidad de probar un método privado, el método no debe ser privado, si se hace público el método te molesta, lo más probable es que sea porque es parte de una responsabilidad separada, debería ser en otra clase ". [ Trabajando eficazmente con Legacy Code (2005) de M. Feathers]


Primero vea si el método debe ser extraído a otra clase y hecho público. Si ese no es el caso, haga que el paquete esté protegido y en Java anote con @VisibleForTesting .


Si está utilizando C #, puede hacer que el método sea interno. De esta forma no contaminará la API pública.

A continuación, agregue atributo a dll

[assembly: InternalsVisibleTo ("MyTestAssembly")]

Ahora todos los métodos están visibles en su proyecto MyTestAssembly. Tal vez no sea perfecto, pero es mejor hacer público el método privado solo para probarlo.


Tiendo a aceptar que las bonificaciones de tenerlo probado en una unidad son mayores que los problemas de aumentar la visibilidad de algunos de los miembros. Una ligera mejora es hacerlo protegido y virtual, luego anularlo en una clase de prueba para exponerlo.

Alternativamente, si la funcionalidad que desea probar por separado no sugiere un objeto faltante de su diseño? Tal vez podrías ponerlo en una clase verificable por separado ... entonces tu clase existente simplemente delega a una instancia de esta nueva clase.


Una prueba unitaria debería probar el contrato público, la única forma en que una clase podría usarse en otras partes del código. Un método privado son los detalles de implementación, no debe probarlo, en la medida en que la API pública funciona correctamente, la implementación no importa y podría cambiarse sin cambios en los casos de prueba.


Utilice la reflexión para acceder a las variables privadas si es necesario.

Pero realmente, no le importa el estado interno de la clase, solo quiere probar que los métodos públicos le devuelven lo que espera en las situaciones que puede anticipar.


en términos de pruebas unitarias, definitivamente no deberías agregar más métodos; Creo que sería mejor que hicieras un caso de prueba sobre tu first() método first() , que se llamaría antes de cada prueba; luego puede llamar varias veces a la next() , previous() y last() para ver si los resultados coinciden con sus expectativas. Supongo que si no agrega más métodos a su clase (solo para fines de prueba), se apegará al principio de prueba de "caja negra";


Actualización : He agregado una respuesta más amplia y más completa a esta pregunta en muchos otros lugares. Esto se puede encontrar en mi blog .

Si alguna vez necesito hacer algo público para probarlo, generalmente esto insinúa que el sistema bajo prueba no está siguiendo el Principio de Responsabilidad Individual . Por lo tanto, hay una clase faltante que debe ser presentada. Después de extraer el código en una nueva clase, hágalo público. Ahora puede hacer pruebas fácilmente y está siguiendo a SRP. Su otra clase simplemente tiene que invocar esta nueva clase a través de la composición.

Hacer que los métodos sean públicos / usar trucos de lenguaje como marcar el código como visible para probar ensambles siempre debe ser un último recurso.

Por ejemplo:

public class SystemUnderTest { public void DoStuff() { // Blah // Call Validate() } private void Validate() { // Several lines of complex code... } }

Refactorice esto introduciendo un objeto validador.

public class SystemUnderTest { public void DoStuff() { // Blah validator.Invoke(..) } }

Ahora todo lo que tenemos que hacer es probar que el validador se invoque correctamente. El proceso real de validación (la lógica previamente privada) puede probarse en aislamiento puro. No habrá necesidad de configuración de prueba compleja para garantizar que esta validación pase.


Como se señala ampliamente por los comentarios de los demás, las pruebas unitarias deben centrarse en la API pública. Sin embargo, aparte de los pros y contras y la justificación, puede llamar a los métodos privados en una prueba unitaria mediante el uso de la reflexión. Por supuesto, debe asegurarse de que su seguridad JRE lo permita. Llamar a métodos privados es algo que Spring Framework emplea con su ReflectionUtils (ver el makeAccessible(Method)método).

Aquí hay una pequeña clase de ejemplo con un método de instancia privado.

public class A { private void doSomething() { System.out.println("Doing something private."); } }

Y una clase de ejemplo que ejecuta el método de instancia privada.

import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class B { public static final void main(final String[] args) { try { Method doSomething = A.class.getDeclaredMethod("doSomething"); A o = new A(); //o.doSomething(); // Compile-time error! doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException! doSomething.invoke(o); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } } }

Ejecutando B, se imprimirá. Doing something private.Si realmente lo necesita, la reflexión podría usarse en pruebas unitarias para acceder a métodos de instancia privados.


En .Net hay una clase especial llamada PrivateObjectdiseñada específicamente para permitirle acceder a métodos privados de una clase.

Obtenga más información al respecto en MSDN o aquí en

(Me pregunto si nadie lo ha mencionado hasta ahora).

Sin embargo, hay situaciones en las que esto no es suficiente, en cuyo caso tendrá que usar la reflexión.

Aún así, me adheriría a la recomendación general de no probar métodos privados, sin embargo, como de costumbre, siempre hay excepciones.


Personalmente, tengo los mismos problemas cuando pruebo métodos privados y esto se debe a que algunas de las herramientas de prueba son limitadas. No es bueno que su diseño sea impulsado por herramientas limitadas, si no responden a su necesidad, cambie la herramienta, no el diseño. Debido a que su pregunta por C # no puedo proponer buenas herramientas de prueba, pero para Java hay dos herramientas poderosas: TestNG y PowerMock, y usted puede encontrar las herramientas de prueba correspondientes para la plataforma .NET


Por lo general, dejo esos métodos protectedy coloco la prueba unitaria dentro del mismo paquete (pero en otro proyecto o carpeta de origen), donde pueden acceder a todos los métodos protegidos porque el cargador de clases los colocará dentro del mismo espacio de nombres.