entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

2votos

¿Cómo eliminar dependencias a clases de terceros en un test harness?

Me gustaría conocer cómo resolveríais el siguiente escenario.

Tengo una clase TextArea, que utilizainternamente dos clases de una librería de terceros, TextBox3rdParty y Renderer3rdParty. Las dos clases de terceros colaboran entre ellas a la hora de inicializar a mi TextArea.

Quiero configurar un test harness que me permita instanciar mi TextArea e inicializarlo, sin introducir dependencias de terceros en el mismo.

Documentándome un poco, veo que una solución típica es extraer la interfaz que necesitemos de cada clase de terceros, y usar esa interfaz en mi TextArea, de forma que en el test harness usaría sendos mocks de esas interfaces, y eliminaría las dependencias.

Por otro lado, en el código de producción, para que mi TextArea funcione, necesito que use realmente la librería de terceros, Para ello me creo sendos wrappers, TextBox3rdPartyWrapper, y Renderer3rdPartyWrapper, que implementan las interfaces.

En el código de inicialización de mi TextArea, necesito pasar a Renderer una instancia de TextBox. Y aquí viene mi primera duda. Lo que hago en Renderer3rdPartyWrapper, método addTextBox(TextBox* textBox), es un cast de TextBox a TextBox3rdPartyWrapper, y hacer que ésta colabore con el Renderer3rdParty de terceros:

void Renderer3rdWrapper::addTextBox(TextBox* textBox)
{
      TextBox3rdPartyWrapper* textBox3rdPartyWrapper = dynamic_cast<TextBox3rdPartyWrapper*>(textBox);
if (textBox3rdPartyWrapper)
  mRenderer3rdParty->addTextBox(textBox3rdPartyWrapper->getTextBox3drParty());
}

Un diagrama UML explicativo de la situación es el siguiente:

UML Explicativo

Y como últimas preguntas/reflexiones: ¿La técnica de "Wrap and Mock" la usaríais siempre y en todos los casos? ¿Dónde ponéis el límite cuando vuestro software depende tanto de un framework de terceros?

Espero que no sea una pregunta demasiado genérica.
¡Gracias!

1 Respuesta

3votos

Leonardo-Tadei Puntos227320

Hola Roberto,

es un tema complejo que da para debatirlo largo y tendido... así que intentaré hacer un resumen:

Tu planteo es correcto, ya que para hacer test unitarios de clases que tengan dependencias, se deben crear Objetos Mock que devuelvan valores fijos y que sirvan para la ejecución de cada test, De esta forma, el testeo se hace en un ambiente controlado. http://es.wikipedia.org/wiki/Objetos_mock

Por otra parte, si bien no es una ley pero sí es regla general, lo único que pasarás por testeos unitarios serán los Objetos del Modelo, con lo que los Mock se crearán para representar a otros Objetos del Modelo que todavía no estén implementados, o para simular la interacción con el ambiente externo como una DB, un sensor o una entrada del usuario.

Por todo esto, salvo que las bibliotecas/frameworks de terceros que uses sean parte del modelo, se prefiere usar algún patrón de diseño con el que desacoplar las dependencias, llegando al extremo de usar Inyección de Dependencias o "Dependency Transformers".

Cuál es el límite para crear Objetos Mocks? Generalmente no debes hacerte esta pregunta, porque si estás haciendo por ejemplo TDD (Test Drive Development) entonces te habrán entregado la batería de test con los que cumplir, y tendrás que hacer tantos mocks como haga falta para pasarlos a todos.

En tu caso y sin conocer más del diseño del software, si TextArea es parte del Modelo, deberías poder desacoplarlo mejor del Render y del TextBox, cuyos nombres sugieren que son parte de la Vista.

Si en cambio el TextArea es parte de la Vista, lo más adecuado no es hacer un Test Unitario, sino hacer testeo desde la interfaz con un robot que simule la interacción con el usuario: http://en.wikipedia.org/wiki/List_of_GUI_testing_tools

Igual Roberto, no hay una línea definida que separe estos aspectos del testeo: hay buena parte de necesidad, pautas del cliente y gustos.

Saludos cordiales

0voto

roberto_garrido_mart comentado

¡Muy buen aporte el de la inyección de dependencias! Definitivamente tengo que investigar más ese tema. Respecto a mi pregunta de "¿Dónde está el límite?", no me refería exactamente a la creación de mocks, sino a la creación de interfaces + wrappers de clases de terceros. Estamos usando un framework gráfico en C++ (con código dependiente del framework repartido por todo el proyecto), y mi pregunta estaría mejor planteada así "¿tengo que declarar una interfaz, e implementar el wrapper correspondiente de todas y cada una de las clases (al menos de los métodos que usamos de ellas) que usamos de este framework?"

0voto

Leonardo-Tadei comentado

Hola Roberto,

es que con un buen diseño, podrías separar más el Modelo de las bibliotecas de terceros, y no tendrías que craer ni wrappers ni mocks para testearlo!

Te vez forzado a crear un montón de wrappers y mocks actualmente seguramente porque el diseño depende innecesariamente de ellas.

Por ejemplo, si tu TextArea en vez de tener a TextBox3rdParty y a Renderer3rdParty por composición, lo tuvieran por agregación, ya te ahorrarías el wrapper solamente tendrías que escribir el mock. A su vez, si en vez de composición o agregación los usaras mediante una mera asociación, el mock a escribir sería mucho más simple todavía, porque harías mocks que hereden de las clases dependientes y solo sobreescribirías los métodos que vas a testear para que devuelvan valores fijos...

No hay que perder de vista que lo que estás testeando es tu clase TextArea...

Saludos cordiales!

PD: recuerda si lo creés apropiado marcar la respuesta como "buena" si te ha servido para mejorar la base de datos del sitio.

0voto

roberto_garrido_mart comentado

Hola Leonardo,
gracias por tus aportaciones. Como intento representar en el diagrama UML, la aproximación en este caso es composición y no agregación, ya que los objetos contenidos no tienen sentido mas allá de la vida del objeto contenedor, y por lo tanto los destruyo cuando destruyo el TextArea.

Además, las interfaces que creo para los mocks y wrappers, contienen únicamente los métodos que mi código necesita llamar, no todos los de cada clase de terceros.

Por otro lado, y hasta donde yo sé, no existen diferencias en código entre agregación y composición (al menos en C++, que yo sepa), pues esto es simplemente un enfoque más conceptual: http://www.seas.es/blog/informatica/agregacion-vs-composicion-en-diagramas-de-clases-uml/

Dicho esto, agradezco mucho tu aportación, y, aunque no consigo concluir si es bueno o no wrappear las clases de terceros que uso por todo mi código, he avanzado bastante en el tema, y revisaré sin duda el tema de la inyección de dependencias, por si se puede aplicar en este caso. Por tanto marco tu respuesta como buena.

0voto

Leonardo-Tadei comentado

Entiendo por qué lo hiciste por composición, pero eso no significa que sea un buen diseño para este caso.

Discrepo con el artículo que enlazás, en que dice que una copmposición puede tener como cardinalidad solamente 1 o 0-1: por ejemplo en un sistema odontológico, cada paciente tiene por composición sus piezas dentales, y por supuesto tendrá una cardinalidad de 0-32.

Lo que determina si es composición o agregación, como bien decís arriba, es el ciclo de vida de los Objetos: si el ciclo de vida es independiente del contenedor, entonces será agregación, y si es dependiente será composición.

Pero hay otra alternativa que es la asociación, que se usa para expresar cuando un método usa para su funcionamiento a un Objeto de otra clase, y este Objeto solo "vive" lo que tarda en ejecutarse el método.

Para seguir adelante, te recomiendo que leas sobre "desacoplamiento" de Objetos; lo vas a encontrar como Object Decoupling.

Saludos cordiales!

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta