Sé que esta es una práctica debatida, pero supongamos que esta es la mejor opción para mí. Me pregunto cuál es la técnica real para hacer esto. El enfoque que veo es este:
1) Haz que una clase de amigo sea la de la clase cuyo método quiero probar.
2) En la clase amigo, cree uno o varios métodos públicos que llamen a los métodos privados de la clase probada.
3) Prueba los métodos públicos de la clase de amigos.
Aquí hay un ejemplo simple para ilustrar los pasos anteriores:
#include <iostream>
class MyClass
{
friend class MyFriend; // Step 1
private:
int plus_two(int a)
{
return a + 2;
}
};
class MyFriend
{
public:
MyFriend(MyClass *mc_ptr_1)
{
MyClass *mc_ptr = mc_ptr_1;
}
int plus_two(int a) // Step 2
{
return mc_ptr->plus_two(a);
}
private:
MyClass *mc_ptr;
};
int main()
{
MyClass mc;
MyFriend mf(&mc);
if (mf.plus_two(3) == 5) // Step 3
{
std::cout << "Passed" << std::endl;
}
else
{
std::cout << "Failed " << std::endl;
}
return 0;
}
Editar:
Veo que en la discusión que sigue a una de las respuestas la gente se pregunta sobre mi código base.
Mi clase tiene métodos que son llamados por otros métodos; ninguno de estos métodos debería llamarse fuera de la clase, por lo que deberían ser privados. Por supuesto, se podrían poner en un método, pero lógicamente están mucho mejor separados. Estos métodos son lo suficientemente complicados como para justificar las pruebas unitarias y, debido a problemas de rendimiento, es muy probable que tenga que volver a factorizar estos métodos, por lo tanto, sería bueno tener una prueba para asegurarme de que mi refactorización no rompa nada. No soy el único que trabaja en el equipo, aunque soy el único que está trabajando en este proyecto, incluidas las pruebas.
Habiendo dicho lo anterior, mi pregunta no era sobre si es una buena práctica escribir pruebas unitarias para métodos privados, aunque agradezco los comentarios.
fuente
Respuestas:
Una alternativa a amigo (bueno, en cierto sentido) que uso con frecuencia es un patrón que he llegado a conocer como access_by. Es bastante simple:
Ahora, suponga que la clase B participa en la prueba A. Puede escribir esto:
Luego puede usar esta especialización de access_by para llamar a métodos privados de A. Básicamente, lo que hace es poner la responsabilidad de declarar amistad en el archivo de encabezado de la clase que quiere llamar a los métodos privados de A. También le permite agregar amigos a A sin cambiar la fuente de A. Idiomáticamente, también indica a quien lea la fuente de A que A no le indica a B un verdadero amigo en el sentido de extender su interfaz. Por el contrario, la interfaz de A está completa como se indica y B necesita acceso especial a A (las pruebas son un buen ejemplo, también he usado este patrón al implementar enlaces de python boost, a veces una función que debe ser privada en C ++ es útil para exponer en la capa de python para la implementación).
fuente
friend access_by
¿Tiene curiosidad acerca de un caso de uso válido para hacer que el , no sea el primer no amigo suficiente: al ser una estructura anidada, tendría acceso a todo dentro de A? p.ej. coliru.stacked-crooked.com/a/663dd17ed2acd7a3Si es difícil de probar, está mal escrito
Si tiene una clase con métodos privados lo suficientemente complejos como para justificar su propia prueba, la clase está haciendo demasiado. Dentro hay otra clase tratando de salir.
Extraiga los métodos privados que desea probar en una nueva clase (o clases) y haga que sean públicos. Prueba las nuevas clases.
Además de hacer que el código sea más fácil de probar, esta refactorización hará que el código sea más fácil de entender y mantener.
fuente
No deberías probar métodos privados. Período. Las clases que usan su clase solo se preocupan por los métodos que proporciona, no por los que usa debajo del capó para trabajar.
Si le preocupa la cobertura de su código, necesita encontrar configuraciones que le permitan probar ese método privado desde una de las llamadas a métodos públicos. Si no puede hacer eso, ¿cuál es el punto de tener el método en primer lugar? Es simplemente un código inalcanzable.
fuente
Hay algunas opciones para hacer esto, pero tenga en cuenta que (en esencia) modifican la interfaz pública de sus módulos, para darle acceso a los detalles de implementación internos (transformando efectivamente las pruebas unitarias en dependencias de cliente estrechamente acopladas, donde debería tener sin dependencias en absoluto).
puede agregar una declaración de amigo (clase o función) a la clase probada.
puede agregar
#define private public
al comienzo de sus archivos de prueba, antes de#include
marcar el código probado. Sin embargo, en caso de que el código probado sea una biblioteca ya compilada, esto podría hacer que los encabezados ya no coincidan con el código binario ya compilado (y causar UB).podría insertar una macro en su clase probada y decidir más adelante qué significa esa macro (con una definición diferente para probar el código). Esto le permitiría probar elementos internos, pero también permitiría que el código de un cliente de terceros piratee su clase (creando su propia definición en la declaración que agregue).
fuente
Aquí hay una sugerencia cuestionable para una pregunta cuestionable. No me gusta el acoplamiento de un amigo ya que el código lanzado tiene que saber sobre la prueba. La respuesta de Nir es una forma de aliviar eso, pero todavía no me gusta mucho cambiar la clase para que se ajuste a la prueba.
Como no confío en la herencia a menudo, a veces solo protejo los métodos privados y hago que una clase de prueba herede y exponga según sea necesario. La realidad es que la API pública y la API de prueba pueden diferir y seguir siendo distintas de la API privada, lo que lo deja en una especie de vínculo.
Aquí hay un ejemplo práctico para el que recurro a este truco. Escribo código incrustado y confiamos bastante en las máquinas de estado. La API externa no necesariamente necesita saber sobre el estado interno de la máquina de estado, pero la prueba debería (posiblemente) probar la conformidad con el diagrama de la máquina de estado en el documento de diseño. Podría exponer un getter de "estado actual" como protegido y luego dar acceso a la prueba a eso, lo que me permite probar la máquina de estado más completamente. A menudo encuentro este tipo de clase difícil de probar como una caja negra.
fuente
Puede escribir su código con muchas soluciones alternativas para evitar que tenga que usar amigos.
Puedes escribir clases y nunca tener ningún método privado. Todo lo que necesita hacer es realizar funciones de implementación dentro de la unidad de compilación, dejar que su clase los llame y pasar cualquier miembro de datos al que necesiten acceder.
Y sí, significará que puede cambiar las firmas o agregar nuevos métodos de "implementación" sin cambiar su encabezado en el futuro.
Sin embargo, debe sopesar si vale la pena o no. Y mucho dependerá realmente de quién va a ver tu encabezado.
Si estoy usando una biblioteca de terceros, preferiría no ver declaraciones de amigos a sus evaluadores de unidades. Tampoco quiero construir su biblioteca y ejecutar sus pruebas cuando lo haga. Desafortunadamente, muchas bibliotecas de código abierto de terceros que he construido hacen eso.
La prueba es el trabajo de los escritores de la biblioteca, no de sus usuarios.
Sin embargo, no todas las clases son visibles para el usuario de su biblioteca. Muchas clases son "implementación" y usted las implementa de la mejor manera para garantizar que funcionen correctamente. En esos, aún puede tener miembros y métodos privados, pero desea que los probadores de unidades los prueben. Así que adelante y hágalo de esa manera si eso conducirá a un código robusto más rápido, eso es fácil de mantener para aquellos que necesitan hacerlo.
Si los usuarios de su clase están todos dentro de su propia empresa o equipo, también puede relajarse un poco más sobre esa estrategia, suponiendo que esté permitida por los estándares de codificación de su empresa.
fuente