David Holm
David Holm

Reputation: 17970

How do I mock objects without inheritance (in C)?

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..

Upvotes: 6

Views: 4400

Answers (7)

donfiguerres
donfiguerres

Reputation: 103

To add to Ilya's answer. You can do this.

#define tcp_socket_send tcp_socket_send_moc
#include "your_source_code.c"

int tcp_socket_send_moc(...)
{ ... }

I use the technique of including the source file into the unit testing module to minimize modifications in the source file when creating unit tests.

Upvotes: 0

Jordfr&#228;s
Jordfr&#228;s

Reputation: 9163

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. stubs, 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.

Upvotes: 2

Martin
Martin

Reputation: 11

Alternatively, you can use TestApe TestApe Unit testing for embedded software - It can do it, but note it is C only. It would go like this -->

int mock_foo_transmit_command(Foo foo, enum Command command) {
  VALIDATE(foo, a);
  VALIDATE(command, b);
}

void test(void) {
  EXPECT_VALIDATE(foo_transmit_command, mock_foo_transmit_command);
  foo_transmit_command(a, b);
}

Upvotes: 1

Yefei
Yefei

Reputation: 71

Use Macro to refine tcp_socket_send is good. But the mock only returns one behavior. Or you need implement some variable in the mock function and setup it differently before each test case. Another way is to change tcp_socket_send to function point. And points it to different mock function for different test case.

Upvotes: 0

Ilya
Ilya

Reputation: 3138

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

Upvotes: 3

Jack
Jack

Reputation:

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

Upvotes: 0

qrdl
qrdl

Reputation: 34998

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.

Upvotes: 0

Related Questions