Utilizamos un modelo de objeto simple para nuestro código de red de bajo nivel en el trabajo donde los punteros struct se pasan a las funciones que se hacen pasar por métodos. Heredé la mayor parte de este código, que fue escrito por consultores con una experiencia C/C++ pasable en el mejor de los casos y he pasado muchas noches tratando de refactorizar el código en algo que se asemejaría a una estructura razonable.¿Cómo me burlo de objetos sin herencia (en C)?
Ahora me gustaría llevar el código bajo prueba unitaria, pero teniendo en cuenta el modelo de objeto que hemos elegido, no tengo idea de cómo simular objetos. Véase el siguiente ejemplo:
cabecera de la muestra (foo.h):
#ifndef FOO_H_
#define FOO_H_
typedef struct Foo_s* Foo;
Foo foo_create(TcpSocket tcp_socket);
void foo_destroy(Foo foo);
int foo_transmit_command(Foo foo, enum Command command);
#endif /* FOO_H_ */
fuente de la muestra (foo.c):
struct Foo_s {
TcpSocket tcp_socket;
};
Foo foo_create(TcpSocket tcp_socket)
{
Foo foo = NULL;
assert(tcp_socket != NULL);
foo = malloc(sizeof(struct Foo_s));
if (foo == NULL) {
goto fail;
}
memset(foo, 0UL, sizeof(struct Foo_s));
foo->tcp_socket = tcp_socket;
return foo;
fail:
foo_destroy(foo);
return NULL;
}
void foo_destroy(Foo foo)
{
if (foo != NULL) {
tcp_socket_destroy(foo->tcp_socket);
memset(foo, 0UL, sizeof(struct Foo_s));
free(foo);
}
}
int foo_transmit_command(Foo foo, enum Command command)
{
size_t len = 0;
struct FooCommandPacket foo_command_packet = {0};
assert(foo != NULL);
assert((Command_MIN <= command) && (command <= Command_MAX));
/* Serialize command into foo_command_packet struct */
...
len = tcp_socket_send(foo->tcp_socket, &foo_command_packet, sizeof(foo_command_packet));
if (len < sizeof(foo_command_packet)) {
return -1;
}
return 0;
}
En el ejemplo anterior me gustaría burlarse de la TcpSocket objeto para que pueda traer "foo_transmit_command" bajo prueba unitaria, pero no estoy seguro de cómo hacerlo sin herencia. Realmente no quiero rediseñar el código para usar vtables a menos que realmente tenga que hacerlo. Tal vez hay un mejor enfoque para esto que burlarse?
Mi experiencia en pruebas proviene principalmente de C++ y tengo un poco de miedo de haberme arrinconado aquí. Agradecería cualquier recomendación de probadores más experimentados.
Editar:
Como Richard Quirk señaló que es realmente la llamada a "tcp_socket_send" que quiero anular y yo preferiría hacerlo sin quitar el símbolo tcp_socket_send real desde la biblioteca al vincular la prueba ya es llamado por otras pruebas en el mismo binario.
estoy empezando a pensar que no hay una solución obvia a este problema ..
Probablemente podría ponerlo en el encabezado TcpSocket y encerrarlo en #ifdef TESTING .. #endif. Buena sugerencia. –
Conoces mejor tu código, solo quería decir que la macro es una cosa total y que la macro colocada en el lugar incorrecto puede llevar al problema que realmente es difícil de depurar – Ilya