Cómo probar una función estática

Al aplicar la prueba unitaria a algún código C, nos topamos con el problema de que no se puede llamar a ninguna función estática en el archivo de prueba, sin modificar el código fuente. ¿Hay alguna manera simple o razonable de superar este problema?

Tengo un arnés de prueba. En casos extremos, como intentar probar una función estática, utilizo:

#include "code_under_test.c" ...test framework... 

Es decir, incluyo todo el archivo que contiene la función bajo prueba en el arnés de prueba. Es un último recurso, pero funciona.

¿Puede dar más información sobre por qué no puede llamar a la función?

¿No está disponible porque es privado para un archivo .c? Si es así, lo mejor es usar una comstackción condicional que permita el acceso a la función para permitir que otras unidades de comstackción accedan a ella. Por ejemplo

SomeHeaderSomewher.h

 #if UNIT_TEST #define unit_static #else #define unit_static static #endif 

Foo.h

 #if UNIT_TEST void some_method #endif 

Foo.cpp

 unit_static void some_method() ... 

Para las pruebas unitarias, en realidad tenemos el código de prueba dentro del archivo fuente en sí y lo comstackmos de manera condicional en la prueba. Esto le da a la unidad pruebas de acceso completo a todas las funciones y variables de nivel de archivo (estáticas o no).

Las pruebas de la unidad en sí no son estáticas; esto nos permite llamar a las pruebas de la unidad desde un único progtwig de superprueba que prueba todas las unidades de comstackción.

Cuando enviamos el código, comstackmos condicionalmente las pruebas unitarias, pero esto en realidad no es necesario (si quiere estar seguro de que está enviando exactamente el mismo código que probó).

Siempre nos pareció invaluable tener las pruebas unitarias en el mismo lugar que el código que está probando, ya que hace más obvio que necesita actualizar las pruebas si cambia el código.

No, no se puede probar directamente una función estática sin modificar al menos un poco la fuente (esa es la definición de estática en C, que no se puede invocar desde una función en un archivo diferente).

¿Podría crear una función separada dentro del archivo de prueba que simplemente llame a la función estática?

Por ejemplo:

 //Your fn to test static int foo(int bar) { int retVal; //do something return retVal; } //Wrapper fn int test_foo(int bar) { return foo(bar); } 

Por lo general, no probamos nuestras funciones estáticas directamente, sino que nos aseguramos de que la lógica que realizan se prueba adecuadamente mediante diferentes pruebas de la función de llamada.

Puede agregar una función no estática para llamar a la función estática y luego llamar a la función no estática.

 static int foo () { return 3; } #ifdef UNIT_TEST int test_foo () { if (foo () == 3) return 0; return 1; } #endif 

las funciones estáticas son esencialmente funciones auxiliares para las funciones públicas (es decir, expuestas). Entonces, IMO, las pruebas unitarias deben llamar a la interfaz pública con entradas que ejercen todas las rutas en la función estática.

La salida (valores de retorno / efectos secundarios) de la función pública se debe usar para probar el efecto de la estática.

Esto significa que necesita tener talones apropiados para ‘atrapar’ estos efectos secundarios. (por ejemplo, si una función llama al archivo IO, debe proporcionar resguardos para anular estas funciones de archivo IO lib). La mejor manera de hacerlo es hacer que cada conjunto de pruebas sea un proyecto / ejecutable separado y evitar el enlace a cualquier función lib externa. Puede simular incluso las funciones C, pero no vale la pena el esfuerzo.

De todos modos, este es el enfoque que he usado hasta ahora y funciona para mí. Buena suerte

Si se encuentra en un entorno Unix, puede incluir en el archivo de prueba el encabezado adicional yourheader_static.h con declaraciones de sus funciones estáticas y traducir el archivo obj code_under_test.o través de objdump --globalize-symbols=syms_name_file para globalizar los símbolos locales. Serán visibles como si fueran funciones no estáticas.

 #define static 

Esta es una muy mala idea. Si tiene una variable declarada local para una función, cambia el comportamiento de la función. Ejemplo:

 static int func(int data) { static int count = 0; count += data; return count; } 

Puede llamar a la función desde la prueba unitaria, ya que se exportaría func (), sin embargo, se modificaría la funcionalidad básica del código.

–kurt

Si usa Ceedling e intenta utilizar el método #include “code_under_test.c”, la comstackción de prueba fallará porque automáticamente intentará comstackr “code_under_test.c” una vez cuando #incluido y también porque es el objective de la prueba .

Pude sortearlo con una pequeña modificación en el código code_under_test.c y un par de cambios más. Envuelva todo el archivo code_under_test.c con esta comprobación:

 #if defined(BUILD) ... #endif // defined(BUILD) 

Agregue esto a su arnés de prueba:

 #define BUILD #include "code_under_test.c" 

Agregue la definición BUILD a su Makefile o archivo de configuración del proyecto:

 # Makefile example .. CFLAGS += -DBUILD .. 

Su archivo ahora se comstackrá a partir de su entorno y cuando se incluya desde su arnés de prueba. Ceedling ahora no podrá construir el archivo por segunda vez (asegúrese de que su archivo project.yml NO define BUILD).

Todas las respuestas sugeridas anteriormente (excepto algunas) sugieren una comstackción condicional que requiere modificación a la fuente. Como tal, eso no debería ser un problema, simplemente agregaría desorden (solo para probar). Más bien puedes hacer algo como esto.

Supongamos que tu función para ser probada es

 static int foo(int); 

Se crea otro archivo de cabecera llamado testing_headers.h que tendrá los contenidos:

 static in foo(int); int foo_wrapper(int a) { return foo(a); } 

Ahora, mientras comstack su archivo c para probar, puede forzar incluir este encabezado desde las opciones del comstackdor.

Para clang y gcc, la bandera es --include . Para el comstackdor de Microsoft C es /FI .

Esto requerirá absolutamente 0 cambio a su archivo c y podrá escribir un contenedor no estático para su función.

Si no desea un contenedor no estático, también puede crear un puntero de función global no estático inicializado para foo.

A continuación, puede llamar a la función utilizando este puntero de función global.

Hay 2 formas de hacer esto.

  1. Incluya el archivo fuente c en el archivo fuente de prueba de la unidad, por lo que el método estático ahora está en el scope del archivo fuente de prueba de la unidad y se puede llamar.

  2. Haz un truco:

#define static

en la cabeza del archivo fuente de prueba de la unidad.

Convertirá la palabra clave static en “nada” o puedo decir que elimina la static de su código fuente c.

En alguna herramienta de prueba de unidades, podemos usar la opción de configuración “pre-procesador” para hacer este truco, solo ingrese la configuración:

static=

La herramienta convertirá todas las palabras clave static a “nada”

Pero debo decir que al usar estas formas el método static (o variable) pierde su significado específico.

Solo para agregar a la respuesta aceptada por Jonathan Leffler, y elaborar sobre la mención de otra de una función de envoltura:

  1. Cree un archivo fuente de prueba, como en test_wrapper_foo.c, donde foo.c es el original.
  2. En test_wrapper_foo.c:
 #include "foo.c" // Prototype int test_wrapper_foo(); // wrapper function int test_wrapper_foo() { // static function to test return foo(); } 

Asumiendo que la función estática foo en foo.c tiene un prototipo: int foo (void);

  1. comstack test_wrapper_foo.c a través de tu archivo MAKE en vez de foo.c (ten en cuenta que esto no romperá ninguna dependencia de las funciones en foo.c por otras funciones externas)

  2. En el script de prueba de la unidad, llame a test_wrapper_foo () en lugar de a foo ().

Este enfoque deja la fuente original intacta, al tiempo que le da acceso a la función desde su marco de prueba.