Как лучше unit test класс, который использует boost:: asio:: yield_context?

У меня есть класс, который использует boost: asio:: yield_context, и мне интересно, как лучше всего unit test его. Упрощенная версия класса, который у меня есть:

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

Я написал собственный "планировщик", который обертывает функциональные возможности boost asio, поэтому у меня есть шанс перехватить вызовы, прежде чем они на самом деле ударят asio. Важно, чтобы тест был детерминированным, поэтому я хотел бы иметь возможность использовать только один поток (так что никогда не вызывайте boost:: asio:: spawn) и в идеале проверяйте этот класс синхронно с некоторым кодом, подобным этому:

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);
 } 
}

Если я создам файл basic_yield_context вручную, могу ли я передать его в Foo:: Read в тесте и работать ли он так, как ожидалось? Если это так, я немного смущен тем, что в этом сценарии действительно ищет basic_yield_context ctor. Если это не сработает, мне действительно интересна лучшая стратегия для тестирования такого кода, какой был бы лучший способ?

Спасибо!

2 ответа

Моим советом было бы использовать макет-объект для имитации интерфейса yield_context.

Есть несколько библиотек, которые вы могли бы попробовать насмехаться.

Хорошим вариантом для повышения является черепаха:

http://turtle.sourceforge.net/

GoogleMock - это еще одна возможность:

https://code.google.com/p/googlemock/

Чтобы использовать одну из этих издевательских библиотек, вам нужно будет изменить свои интерфейсы для размещения макетов. Я думаю, что в этом случае лучше всего реализовать методы для Read и MyScheduler::WaitOnEvent, поэтому тип параметра контекста является параметром шаблона (т.е. Утиным типом):

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...>
};
</typename>


Отправляясь, чтобы отметить Брэндон, ответьте как правильный: 1) это единственный ответ, который я получил:) и 2) он более общий, чем все, что я смог придумать.

Я не использовал метод утиной печати, потому что он не очень хорошо вписывался в потребности обложки asio, которую я написал. То, что я закончил, действительно продолжало позволять классу использовать реальный io-контекст и реальный контекст доходности, но не привязывать нити к контексту io. Я построил псевдо-временную шкалу запланированных событий, а затем разыграл их по мере необходимости, чтобы результаты были всегда детерминированными. Это может показаться немного ухищренным, но это то, что лучше всего работало в рамках ограничений, с которыми я имел дело.

licensed under cc by-sa 3.0 with attribution.