2011-03-31 14 views
17

Desarrollamos una aplicación C++ usando Visual Studio 2008 y una prueba de unidad usando Boost.Test. Por el momento, tenemos una solución separada que contiene nuestras pruebas unitarias.Unidad probando clases no exportadas en una DLL

Muchos de nuestros proyectos en la solución principal producen DLL. Estamos limitados en la cobertura de prueba porque no podemos evaluar clases no exportadas.

Tengo dos ideas sobre cómo podrían ser probados:

  1. exportación de muebles
  2. ponen las pruebas dentro de la DLL (mismo proyecto y la solución) y el uso corredor externo de Boost.Test

No estoy del todo seguro de cuáles serían los inconvenientes. El número 1 anterior rompe la encapsulación a nivel de módulo, y el número 2 podría dar como resultado una DLL mucho más grande, a menos que solo sea posible incluir el código de prueba en ciertas configuraciones.

Entonces, ¿hay algún inconveniente grave en los métodos anteriores, o puede pensar en otras soluciones?

+3

Me gustaría dar una pista sobre [CMake] (http://www.cmake.org) ofreciendo una característica llamada "bibliotecas de objetos". ('add_library (foo_obj OBJECT ...)') En mis proyectos, construyo las fuentes en bibliotecas de objetos, que luego enlace a * ambas * la DLL ('add_library (foo SHARED ... $ )') * y * sus controladores de prueba ('add_executable (foo_test ... $ )'). Es una variante de las respuestas a continuación usando un sistema de compilación diferente (por lo que agregué esto como un comentario, no como una respuesta), pero está resolviendo el mismo problema. – DevSolar

Respuesta

9

Ampliando la respuesta de Tom Quarendon a this question, he utilizado una ligera variante de la respuesta de Simon Steele:

  • Crear un proyecto de prueba (utilizando cualquier marco de pruebas que te gusta, yo uso CppUnit).
  • En su test_case.cpp, #include <header/in/source/project.h>.
  • En las propiedades del proyecto de prueba:
    • En Linker-> General, agregue $(IntDir) a los directorios de librerías adicionales del proyecto fuente.
    • En Enlazador-> Entrada, agregue los archivos .obj a Dependencias Adicionales.
  • Agregue la dependencia del proyecto de prueba al proyecto fuente en Project-> Project Dependencies.

Nuevamente, el único gasto de mantenimiento es el estándar para las pruebas unitarias: para crear la dependencia de la (s) unidad (es) que desea probar.

+2

Otro detalle sobre este enfoque: si su DLL bajo prueba usa encabezados precompilados, todos los archivos .obj que están vinculados para producir la DLL bajo prueba dependen del archivo de encabezado precompilado de la DLL bajo prueba. Al generar el proyecto de prueba, esto da como resultado el error del enlazador LNK2011: objeto precompilado no vinculado; la imagen no se puede ejecutar. Además de los archivos de objetos específicos que está probando, debe agregar stdafx.obj (si su archivo PCH se genera al compilar stdafx.cpp). –

+0

Otra nota sobre este enfoque: si su proyecto de prueba usa varios archivos DLL que contienen archivos de objetos con nombres idénticos que deben probarse, puede especificar la ruta completa en el Enlazador-> Configuración de entrada, p. Ej. '$ (SolutionDir) \ t \ $ (Platform) \ $ (Configuration) \ .obj' para distinguirlos. – Edward

+0

Es posible que también necesite [/FORCE:MULTIPLE](https://msdn.microsoft.com/en-us/library/70abkas3.aspx) como acabo de descubrir. – Rai

3

La solución que uso para esto es construir el mismo código no exportado en mi DLL de prueba también. Esto aumenta el tiempo de construcción y significa agregar todo a ambos proyectos, pero guarda la exportación de todo o las pruebas en el código principal del producto.

Otra posibilidad sería compilar el código no exportado en una lib utilizada tanto por el archivo DLL exportado como por el proyecto de prueba unitario.

+0

Esto podría funcionar para proyectos pequeños, pero tenemos un montón de código, por lo que sería una pesadilla de mantenimiento tener que hacer cambios en dos lugares. – Jon

+0

Los únicos cambios que tendrían que realizarse son cuando se agregan o eliminan archivos. Entonces, si se agrega un nuevo archivo CPP que contiene un código que necesita ser probado en unidades, entonces debe agregarse a ambos proyectos. No hay dos copias del código fuente, cada archivo fuente que contiene código comprobable solo se incluye en ambos proyectos. –

+0

Este fue mi primer enfoque, que funcionó para mí. Sin embargo, pensé en un problema potencial: a veces un proyecto dll usa diferentes banderas de compilación que el proyecto de prueba.Por lo tanto, los proyectos dll y de prueba podrían crear diferentes archivos de objeto para el mismo archivo fuente. Aunque en mi caso estaba bastante seguro de que eran iguales, en general es más seguro probar los archivos objeto que crea el proyecto dll, en lugar de los archivos objeto que crea el proyecto de prueba. Terminé cambiando mi enfoque al descrito @Rai. –

0

trate de hacer una definen como la siguiente en algún lugar todos los archivos incluirán:

#define EXPORTTESTING __declspec(dllexport) 

y utilizarlo en lugar del dllexport, así:

class EXPORTTESTING Foo 
{ 
... 
}; 

entonces usted será capaz de apague el indicador para compilar un archivo DLL de lanzamiento, pero consérvelo para una DLL comprobable por una unidad.

+2

No estoy seguro de que sea una buena manera de hacerlo ... El código comprobable no debe modificarse para probarse. Incluso si es una simple macro. – toussa

2

Estaba buscando una solución también, tal vez lo siguiente será más fácil de mantener.

Agregue una nueva configuración de compilación, p. "Unit testing Debug" en el proyecto DLL y cambie el tipo de configuración para que sea "Static Library .lib" ("General" -> "Configuration Type").

A continuación, simplemente agregue una dependencia de las pruebas de su unidad en este proyecto, ahora todo debe vincularse cuando use la nueva configuración de compilación "Unit testing Debug". Si está utilizando construcciones de versiones para pruebas unitarias, entonces necesita agregar otra configuración con optimizaciones de versión.

Así que los beneficios de esta solución son:

  • baja maintanability costar
  • proyecto de biblioteca/estático única DLL
  • no tienen que unir manualmente a los archivos .obj

Inconvenientes:

  • Configuración adicional el perfil (s) ración requerirá algunos cambios en su entorno de construcción (IC)
  • Mayores tiempos de compilación

Actualización: realidad Terminamos con un enfoque diferente.

Agregamos nuevas "depuración de prueba"/"configuraciones de liberación de estudio para cada proyecto existente que tenemos.

Para .exe/.dll proyectos desactivamos la main.cpp original a partir de la compilación y lo reemplazó con el uno que ejemplifica el marco de prueba (por ejemplo, gtest) y ejecuta todas las pruebas, las pruebas están en archivos separados .cpp que también se excluyen de la compilación en configuraciones regulares (Release/Debug) y habilitadas solo en configuraciones de prueba.

For .lib proyectos también tenemos nuevas configuraciones de "Prueba de depuración"/"Versión de prueba" y allí convertimos la biblioteca estática en un archivo .exe y proporcionamos un main.cpp que ejemplifica el marco de prueba nd ejecuta las pruebas y las pruebas por sí mismos. Los archivos relacionados con la prueba se excluyen de la compilación en las configuraciones Release/Debug.

Cuestiones relacionadas