Help to test asynchronous API

332 views
Skip to first unread message

Tiago Katcipis

unread,
Oct 22, 2013, 8:31:48 AM10/22/13
to cppu...@googlegroups.com
Hi,

I'm testing some asynchronous APIs that i have to mock (GDBus API) so i don't need interaction through DBus with real processes and don't need a configured dbus-daemon to run my tests. But to exercise the code with these async GDBus APIs i need a way to mock the function and grab the callback provided by the production code, then i can call the callback function, exercising the code (mocking a response from DBus).

My problem is to find a elegant way to transfer the callback function from the mocked function to the test case. Right now I'm using the setData/getData, but since it is global it does impose the requirement to use some kind of namespacing on the name of the data (like "function_name:data_name").

I thought on using the MockSuppport namespacing for this:

http://cpputest.github.io/mocking_manual.html#mock_scope

to write something like:

On the mock:
mock("function_name").setData("callback", c);

On the test:
Callback c = mock("function_name").getData("callback", c);
c();

Is there any other idea to implement this that would be more elegant? It is pretty hard to test these kind of async APIs, i don't know if the mocked function should already call the callback (since this is the most normal case on the tests), of if the communication between the test case and the mocked function is ok.

Any thoughts?

Best regards,
Tiago Katcipis

James Grenning

unread,
Oct 22, 2013, 10:29:38 AM10/22/13
to cppu...@googlegroups.com
Hello Tiago

Why don't you just call the callback function directly to test it?  

If you are checking that the async calls are setup properly, you could mock the GDBus API and check the parameters.  This article series may be similar to what you need, though it uses the fake function framework. http://www.renaissancesoftware.net/blog/?s=rtos&submit=Search

thanks, James

--------------------------------------------------------------------------------------------
James Grenning Author of TDD for Embedded C

--
You received this message because you are subscribed to the Google Groups "cpputest" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cpputest+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Tiago Katcipis

unread,
Oct 22, 2013, 4:01:10 PM10/22/13
to cppu...@googlegroups.com
James,

Thanks for your response (sadly the link is not opening here at my work, for some reason that does not make sense to me :-(, but i will see it at home).

The callback function is not part of the public API of the module I'm testing, for example, i want to test that when you call:

void module_do_stuff(GObject * obj);

It will actually call a DBus method "GetData" and will process this data and do some stuff with it, producing a result :-), the DBus method call is internal (it is actually a detail of implementation, since it is a form of delegation), but if i don't exercise the DBus response i cant check if what i want is actually done (the post processing on the data, producing a result).

For example, when the asynchronous call is done and the processing is also done a signal can be emitted by the GObject, so what i want to test is the module_do_stuff() call and the signal emission (an event, indicating that stuff has been done), these are the parts of the public API of the object, not the callback of the DBus call.

Instead of using signals i could make module_do_stuff() itself being asynchronous, since it will depend on asynchronous resources, but the idea is that the callback of the DBus method is internal and it will still do some post processing on the returned data. Even if i tested the internal callback directly, i would not have a test proving that the whole thing works, on the isolated callback :-(.

Also this means making functions public on the API that are not required to be public at all (the client wont use it, only the tests), so it seems to be wrong (maybe I'm totally wrong with this, but the feeling is that it is wrong :-).


--
You received this message because you are subscribed to a topic in the Google Groups "cpputest" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cpputest/8z51XVSopsE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cpputest+u...@googlegroups.com.

James Grenning

unread,
Oct 22, 2013, 9:59:18 PM10/22/13
to cppu...@googlegroups.com
Can you post some example code?

Tiago Katcipis

unread,
Oct 23, 2013, 9:58:55 AM10/23/13
to cppu...@googlegroups.com
Here goes some pseudo code to explain the problem better, on GDBus even creating a proxy to a DBus object is a asynchronous operation (actually we use async calls to avoid multi-threading and blocking, everything is done on the glib mainloop).

On the context of a VOIP application, lets say i want to test that when a call is received it will be answered:

static void
on_call_proxy_ready (GAsyncResult res, void * userdata)
{
    CallAnswerer * self = CALL_ANSWERER(userdata);
    self->call_proxy = call_new_for_bus_finish(res);
    /* now i can answer the call calling a method and do more stuff.
       this is the code i want to exercise on the test */
    call_answer(self->call_proxy, yet_another_callback_since_it_is_async, self);
    /* and so goes on ... */
}

static void
on_call_received_answer_it (NewCallNotifier * n, const char * call_object_path,
                                          void * userdata)
{
    //here i have to create a proxy to the call, using the object path that is a DBus id of the object.

   call_new_for_bus("well.know.bus.name", call_object_path, on_call_proxy_ready, userdata);
}

CallAnswerer * call_answerer_new(NewCallNotifier * n) {
    CallAnswerer * self = g_object_new(G_TYPE_CALL_ANSWERER, NULL);
    g_signal_connect(n, "call-received", on_call_received_answer_it, self);
    return self;
}

Basically we have a CallAnswerer that monitors a object that only purpose is to notify that a new call is available, when the event happens (the signal) on my callbacks i will handle the events and do all the required stuff (what i want to test).

Usually the DBus method calls are mocked so i can just check if the methods are being called correctly on the right moment, but the event of a new call being received could trigger other events (like monitoring the call proxy to do something when the call is terminated).

As you can see that is no need at all to make the callbacks functions public, the client code of the CallAnswerer is not interested on then.

Since i don't have DBus on the unit tests i mock the call_new_for_bus and call_new_for_bus_finish, but then my problem with callbacks started :-).

I tried to use DBus instead of mocking it, but it will be pretty hard to maintain the tests FIRST.

I hope i was able to express it clearly now.

James Grenning

unread,
Oct 23, 2013, 11:09:52 AM10/23/13
to cppu...@googlegroups.com
On Oct 23, 2013, at 8:58 AM, Tiago Katcipis <katc...@inf.ufsc.br> wrote:


Basically we have a CallAnswerer that monitors a object that only purpose is to notify that a new call is available, when the event happens (the signal) on my callbacks i will handle the events and do all the required stuff (what i want to test).

Usually the DBus method calls are mocked so i can just check if the methods are being called correctly on the right moment, but the event of a new call being received could trigger other events (like monitoring the call proxy to do something when the call is terminated).

As you can see that is no need at all to make the callbacks functions public, the client code of the CallAnswerer is not interested on then.

Since i don't have DBus on the unit tests i mock the call_new_for_bus and call_new_for_bus_finish, but then my problem with callbacks started :-).



It seems that your test-doubles for the DBus can capture and expose the callback function pointers so your test can call them.  Also the test-doubles themselves can call them.

The later is what my article series shows.

Tiago Katcipis

unread,
Oct 23, 2013, 1:50:39 PM10/23/13
to cppu...@googlegroups.com


On Wednesday, October 23, 2013 1:09:52 PM UTC-2, James Grenning wrote:


On Oct 23, 2013, at 8:58 AM, Tiago Katcipis <katc...@inf.ufsc.br> wrote:


Basically we have a CallAnswerer that monitors a object that only purpose is to notify that a new call is available, when the event happens (the signal) on my callbacks i will handle the events and do all the required stuff (what i want to test).

Usually the DBus method calls are mocked so i can just check if the methods are being called correctly on the right moment, but the event of a new call being received could trigger other events (like monitoring the call proxy to do something when the call is terminated).

As you can see that is no need at all to make the callbacks functions public, the client code of the CallAnswerer is not interested on then.

Since i don't have DBus on the unit tests i mock the call_new_for_bus and call_new_for_bus_finish, but then my problem with callbacks started :-).



It seems that your test-doubles for the DBus can capture and expose the callback function pointers so your test can call them.  Also the test-doubles themselves can call them.

The "how" to capture and expose the callback was my original question, it seems that a good way is to use the setData and getData, using the name of the function as a scope for the mock, like:

mock("my_function").setData("callback", callback);

GCallback callback = mock("my_function").getData("callback");

Letting the double call the callback seems to avoid some duplication on most cases, may it will be better.
 

The later is what my article series shows.

I will take a look at it.

Thanks for the help James.

It is a bit off topic, but i feel compelled to say that Test Driven Development for Embedded C is totally awesome and changed the way i develop in C, thanks for that too :-).
 

James Grenning

unread,
Oct 24, 2013, 12:33:49 AM10/24/13
to cppu...@googlegroups.com
On Oct 23, 2013, at 12:50 PM, Tiago Katcipis <tiagok...@gmail.com> wrote:


The "how" to capture and expose the callback was my original question, it seems that a good way is to use the setData and getData, using the name of the function as a scope for the mock, like:


You can just save them in a global variable or data structure and access them directly or via a getter function.  You have the full power of the C language at your disposal. You do not have to involve CppUMock.  In the article, see the custom_fake.  That is part of the fake function framework, but you can do the same thing in your stubs.




mock("my_function").setData("callback", callback);

GCallback callback = mock("my_function").getData("callback");

Letting the double call the callback seems to avoid some duplication on most cases, may it will be better.
 

The later is what my article series shows.

I will take a look at it.

Thanks for the help James.

It is a bit off topic, but i feel compelled to say that Test Driven Development for Embedded C is totally awesome and changed the way i develop in C, thanks for that too :-).
 

Thank you Tiago!

James

Andreas Stenius

unread,
Oct 24, 2013, 8:08:30 AM10/24/13
to cppu...@googlegroups.com
2013/10/22 James Grenning <ja...@grenning.net>
[...]  This article series may be similar to what you need, though it uses the fake function framework. http://www.renaissancesoftware.net/blog/?s=rtos&submit=Search


Thank you James for that link (which also led me to fff, awesome!). I am just about to get into some really old legacy firmware code..

James Grenning

unread,
Oct 24, 2013, 10:37:39 AM10/24/13
to cppu...@googlegroups.com
Thanks. Glad it will help!


Tiago Katcipis

unread,
Oct 25, 2013, 8:05:58 AM10/25/13
to cppu...@googlegroups.com


On Thursday, October 24, 2013 2:33:49 AM UTC-2, James Grenning wrote:


On Oct 23, 2013, at 12:50 PM, Tiago Katcipis <tiagok...@gmail.com> wrote:


The "how" to capture and expose the callback was my original question, it seems that a good way is to use the setData and getData, using the name of the function as a scope for the mock, like:


You can just save them in a global variable or data structure and access them directly or via a getter function.  You have the full power of the C language at your disposal. You do not have to involve CppUMock.  In the article, see the custom_fake.  That is part of the fake function framework, but you can do the same thing in your stubs.

Usually i avoid the C super power of having global variables or data structure, even if they are global only inside the test module. My problem with that is that i will have to perform setup/teardown on these global variables, any failure on doing that and i will get nasty non isolated tests.

If i use CppUMock, it has that beautiful MockPlugin that cleans up everything for each test, so i get a clean mock environment for free on each test, without writing a single line of code :-).

And using CppUMock it is also easier to write test infrastructre that can be used across projects (some of these async calls may be mocked on more than one project, but on the same fashion, so there is a possibility for reuse here).

That's why I'm so interested on finding ways to use CppUMock to do the job for me :-).

 

Tiago Katcipis

unread,
Nov 15, 2013, 10:36:03 AM11/15/13
to cppu...@googlegroups.com
James,

I finally read the RTOS articles and they are very interesting, i didn't know the FFF project and it is a good example of how to TDD async stuff.

But it is not exactly my problem, if i understood the articles correctly you are testing asynchronous stuff, but it is handled by multithreading and blocking on semaphores to wait the data to be available.

On my case it is event driven asynchorous, based on a main loop (glib mainloop). There is no locking on semaphores or waiting anything explicitly, you just call something and pass a callback, when it is done, the callback is called (there is some comments on the article saying that this model is more adaptable to the open/closed principle).

But as you said, i can just use plain C, but using the CppUTest mock setData seems to be a good way too, I'm just interested on alternatives besides that, or if CppUTest has some kind of neat trick to handle this cases.

For now I'm using setData and calling the callback inside the mock, it seems to work ;-).

Thanks for all the help and the great articles.
Reply all
Reply to author
Forward
0 new messages