views:

697

answers:

4

We use a simple object model for our low level networking code at work where struct pointers are passed around to functions which are pretending to be methods. I've inherited most of this code which was written by consultants with passable C/C++ experience at best and I've spent many late nights trying to refactor code into something that would resemble a reasonable structure.

Now I would like to bring the code under unit testing but considering the object model we have chosen I have no idea how to mock objects. See the example below:

Sample header (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_ */

Sample source (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;
}

In the example above I would like to mock the TcpSocket object so that I can bring *"foo_transmit_command"* under unit testing but I'm not sure how to go about this without inheritance. I don't really want to redesign the code to use vtables unless I really have to. Maybe there is a better approach to this than mocking?

My testing experience comes mainly from C++ and I'm a bit afraid that I might have painted myself into a corner here. I would highly appreciate any recommendations from more experienced testers.

Edit:
Like Richard Quirk pointed out it is really the call to *"tcp_socket_send"* that I want to override and I would prefer to do it without removing the real tcp_socket_send symbol from the library when linking the test since it is called by other tests in the same binary.

I'm starting to think that there is no obvious solution to this problem..

A: 

Not sure what you want to achieve.

You can add all foo_* functions as function pointer members to struct Foo_s but you still need to explicitly pass pointer to your object as there is no implicit this in C. But it will give you encapsulation and polymorphism.

qrdl
That would require me to rewrite a large portion of the code base "just" to get the code into unit tests, which I'd preferably avoid.
David Holm
A: 

What OS are you using? I believe you could do an override with LD_PRELOAD on GNU/Linux: This slide looks useful.

Linux but in some cases dependencies all live in the same library so if I preload another library I would also override the object I'm testing.
David Holm
No, linker would only find the one symbol -- tcp_socket_send -- in LD_PRELOAD, others would all be linked as usual.
c-urchin
+2  A: 

You can use macro to redefine tcp_socket_send to tcp_socket_send_moc and link with real tcp_socket_send and dummy implementation for tcp_socket_send_moc.
you will need to carefully select the proper place for :

#define tcp_socket_send tcp_socket_send_moc
Ilya
I could probably put it in the TcpSocket header and enclose it in #ifdef TESTING .. #endif. Good suggestion.
David Holm
You know your code better i just wanted to state the macro is a total thing and macro placed in wrong place can lead to the problem that really hard to debug
Ilya
+1  A: 

Have a look at TestDept: http://code.google.com/p/test-dept/

It is an open source project that aims at providing possiblity to have alternative implementations, e.g. mocks, of functions and being able to change in run-time which implementation of said function to use.

It is all accomplished by mangling object files which is very nicely described on the home page of the project.

Jordfräs