What's the best way to unit test a class that uses boost :: asio :: yield_context?
I have a class that uses boost: asio :: yield_context and I am wondering what is the best way to unit test it. A simplified version of the class I have:
class Foo {
public:
void Read(boost::asio::yield_context context) {
my_scheduler->WaitOnEvent(BUFFER_HAS_DATA, context);
<...snip...>
callback(data);
}
void Write() {
// write to buffer
my_scheduler->FireEvent(BUFFER_HAS_DATA);
}
void Start() {
my_scheduler->Spawn(boost::bind(&Foo::Read, this, _1));
}
<...snip...>
};
I wrote a custom "scheduler" that wraps the boost asio functionality so I have a chance to intercept the calls before they actually hit asio. It is important that the test is deterministic, so I would like to be able to use only one thread (so never call boost :: asio :: spawn) and ideally test this class in sync with some code like this:
void do_test() {
<...snip...>
unsigned int num_callbacks = 0;
auto callback = [&num_callbacks] (data) {
++num_callbacks;
}
foo->SetCallback(callback);
for (int i = 1; i <= 5; ++i) {
foo->Write();
foo->Read(); // What would I need to pass here?
assert(num_callbacks == i);
}
}
If I create the basic_yield_context file manually, can I pass it to Foo :: Read in the test and does it work as expected? If so, I am a little confused as to what the ctor is actually looking for in this scenario for basic_yield_context. If that doesn't work, I'm really curious about the best strategy for testing this kind of code, what would be the best way?
Thank!
source to share
My advice would be to use a mock object to simulate an interface yield_context
.
There are several libraries you could try to mock.
A good option for boosting is turtle:
http://turtle.sourceforge.net/
GoogleMock is another feature:
https://code.google.com/p/googlemock/
To use one of these mocking libraries, you will need to modify your interfaces to accommodate layouts. I think in this case it is best to implement methods for Read
and MyScheduler::WaitOnEvent
, so the type of the context parameter is a template parameter (i.e. Duck type):
class Foo {
public:
//! This would be a `duck-type` interface.
template <typename YieldContext>
void Read(YieldContex context) {
//! You would also need one for your my_scheduler type call to WaitOnEvent.
my_scheduler->WaitOnEvent(BUFFER_HAS_DATA, context);
<...snip...>
callback(data);
}
<...snip...>
};
source to share
Going to tag Brandon, answer as correct: 1) this is the only answer I got :) and 2) it is more general than anything I could think of.
I didn't use the duck printing method because it didn't fit very well with the needs of the asio cover I wrote. What I ended up doing was actually continuing to let the class use the real io context and the real yield context, but not bind the threads to the io context. I built a pseudo-timeline of scheduled events and then played them out as needed so that the results were always deterministic. This may sound a little tricky, but this is what worked best within the constraints I was dealing with.
source to share