william resueltos probabilidad mendenhall introduccion estadística estadistica esencial elemental ejercicios ala c++ unit-testing oop encapsulation friend

c++ - estadística - introduccion ala probabilidad y estadistica william mendenhall ejercicios resueltos



¿Qué hay de malo en hacer que un examen de unidad sea un amigo de la clase que está probando? (7)

En C ++, a menudo he convertido a una clase de prueba de unidad en una amiga de la clase que estoy probando. Hago esto porque a veces siento la necesidad de escribir una prueba unitaria para un método privado, o tal vez quiero acceder a algún miembro privado para que pueda configurar más fácilmente el estado del objeto para que pueda probarlo. Para mí, esto ayuda a preservar la encapsulación y la abstracción porque no estoy modificando la interfaz pública o protegida de la clase.

Si compro una biblioteca de terceros, no me gustaría que su interfaz pública se contamine con un montón de métodos públicos que no necesito saber simplemente porque el proveedor quería una prueba unitaria.

Tampoco quiero tener que preocuparme por un grupo de miembros protegidos que no necesito saber si estoy heredando de una clase.

Es por eso que digo que preserva la abstracción y la encapsulación.

En mi nuevo trabajo, fruncen el ceño en contra de usar clases de amigos incluso para pruebas unitarias. Dicen que la clase no debe "saber" nada sobre las pruebas y que no desea un acoplamiento estricto de la clase y su prueba.

¿Puede alguien explicarme estas razones más para que pueda entender mejor? Simplemente no veo por qué usar un amigo para las pruebas unitarias es malo.


Al igual que bcat sugirió, en la medida de lo posible, necesita encontrar errores utilizando la interfaz pública en sí. Pero si desea hacer cosas como imprimir variables privadas y comparar con el resultado esperado, etc. (Útil para que los desarrolladores resuelvan fácilmente los problemas), entonces puede hacer que la clase UnitTest como friend to class sea probada. Pero es posible que deba agregarlo debajo de una macro como a continuación.

class Myclass { #if defined(UNIT_TEST) friend class UnitTest; #endif };

Habilite el indicador UNIT_TEST solo cuando se requiera una prueba unitaria. Para otras versiones, necesita deshabilitar esta bandera.


Debería considerar que existen diferentes estilos y métodos para probar: la prueba de recuadro negro solo prueba la interfaz pública (tratando la clase como una caja negra). Si tienes una clase base abstracta, incluso puedes usar las mismas pruebas contra todas tus implementaciones.

Si usa las pruebas de caja blanca , incluso podría ver los detalles de la implementación. No solo sobre qué métodos privados tiene una clase, sino qué tipo de declaraciones condicionales se incluyen (es decir, si desea aumentar la cobertura de su condición porque sabe que las condiciones eran difíciles de codificar). En las pruebas de caja blanca, definitivamente tiene un "alto acoplamiento" entre las clases / implementación y las pruebas que son necesarias porque desea probar la implementación y no la interfaz.

Como señaló bcat, a menudo es útil usar composición y más, pero clases más pequeñas en lugar de muchos métodos privados. Esto simplifica las pruebas de caja blanca porque puede especificar más fácilmente los casos de prueba para obtener una buena cobertura de prueba.


Haga que las funciones que desea probar estén protegidas. Ahora en su archivo de prueba de unidad, cree una clase derivada. Crea funciones de contenedor público que llamen a tus funciones protegidas de clase bajo prueba.


Idealmente, no debería necesitar probar los métodos privados de prueba en absoluto. Todo lo que un consumidor de tu clase debe preocuparse es la interfaz pública, así que eso es lo que debes probar. Si un método privado tiene un error, debe ser capturado por una prueba unitaria que invoque algún método público en la clase que eventualmente termine llamando al método privado con errores. Si un error se escapa, esto indica que los casos de prueba no reflejan completamente el contrato que desea que implemente su clase. La solución a este problema es, casi con seguridad, probar los métodos públicos con más escrutinio, no hacer que sus casos de prueba investiguen los detalles de implementación de la clase.

De nuevo, este es el caso ideal. En el mundo real, las cosas pueden no siempre ser tan claras, y tener una clase de prueba de unidad para ser un amigo de la clase que prueba podría ser aceptable, o incluso deseable. Aún así, probablemente no sea algo que quieras hacer todo el tiempo. Si parece surgir con suficiente frecuencia, podría ser una señal de que sus clases son demasiado grandes y / o realizan demasiadas tareas. Si es así, subdividirlos aún más refaccionando conjuntos complejos de métodos privados en clases separadas debería ayudar a eliminar la necesidad de pruebas unitarias para conocer los detalles de la implementación.


No veo nada de malo en utilizar una clase de prueba de unidad de amigo en muchos casos. Sí, descomponer una clase grande en clases más pequeñas a veces es una mejor manera de hacerlo. Creo que las personas son un poco apresuradas para descartar el uso de la palabra clave friend para algo como esto: puede que no sea el diseño ideal orientado a objetos, pero puedo sacrificar un poco de idealismo para una mejor cobertura de prueba si eso es lo que realmente necesito.


Normalmente, solo prueba la interfaz pública para que pueda rediseñar y refactorizar la implementación. Agregar casos de prueba para miembros privados define un requisito y una restricción en la implementación de su clase.


Siento que Bcat dio una muy buena respuesta, pero me gustaría exponer sobre el caso excepcional que alude a

En el mundo real, las cosas pueden no siempre ser tan claras, y tener una clase de prueba de unidad para ser un amigo de la clase que prueba podría ser aceptable, o incluso deseable.

Trabajo en una empresa con una gran base de código heredada, que tiene dos problemas que contribuyen a hacer deseable la prueba unitaria de un amigo.

  • Sufrimos de funciones y clases obscenamente grandes que requieren refactorización, pero para poder refactorizar es útil realizar pruebas.
  • Gran parte de nuestro código depende del acceso a la base de datos, que por diversas razones no debería incluirse en las pruebas unitarias.

En algunos casos, Mocking es útil para aliviar este último problema, pero muy a menudo esto solo lleva a un diseño complejo (jerarquías de clase donde no sería necesario), mientras que uno podría simplemente refactorizar el código de la siguiente manera:

class Foo{ public: some_db_accessing_method(){ // some line(s) of code with db dependance. // a bunch of code which is the real meat of the function // maybe a little more db access. } }

Ahora tenemos la situación en la que la carne de la función necesita una refactorización, por lo que nos gustaría una prueba unitaria. No debe exponerse públicamente. Ahora, hay una maravillosa técnica llamada burla que podría usarse en esta situación, pero el hecho es que en este caso una burla es exagerada. Me requeriría aumentar la complejidad del diseño con una jerarquía innecesaria.

Un enfoque mucho más pragmático sería hacer algo como esto:

class Foo{ public: some_db_accessing_method(){ // db code as before unit_testable_meat(data_we_got_from_db); // maybe more db code. } private: unit_testable_meat(...); }

Este último me da todos los beneficios que necesito de las pruebas unitarias, que incluyen darme esa preciosa red de seguridad para detectar errores producidos cuando refactorizo ​​el código en la carne. Para probarlo en una unidad, tengo que ser amigo de una clase UnitTest, pero estoy firmemente convencido de que esto es mucho mejor que una jerarquía de código inútil para permitirme usar un Mock.

Creo que esto debería convertirse en una expresión idiomática, y creo que es una solución adecuada y pragmática para aumentar el ROI de las pruebas unitarias.