Я столкнулся с ситуацией, когда мы используем http::async_read(...) и http::async_write(...) внутри класса, который представляет сеанс HTTP. Однако, если мы хотим провести модульное тестирование этого класса, мы бы предпочли не открывать сетевое соединение с портом и сокетом для прослушивания и, соответственно, принятия первого вызова через acceptor.
Скорее, я надеялся «подделать» поведение socket с помощью другого AsyncReadStream. Я хотел написать свой собственный, но это кажется сложной задачей, и поиск не дал особого результата.
Какой пример AsyncReadStream может быть здесь полезен для простой потоковой передачи запроса HTTP и последующего ввода ответа в целях модульного тестирования? Если нет, есть ли какой-нибудь шаблон, который я мог бы использовать для написания простого шаблона с этой очень минимальной «фальшивой» функциональностью?
Я надеюсь не решать эту проблему жестоко @pptaszni. Решение, очевидно, состоит в том, чтобы сделать то, что вы говорите, и вызвать их через какой-то http_interface, который может быть либо макетом, либо prod., и prod. внизу вызывал бы описанные мной функции (в отличие от того, чтобы они были встроены и вызывались напрямую как есть), но я надеюсь на способ оставить упомянутые мной функции и просто вместо этого использовать «макет» AsyncReadStream, как я описал.
@pptaszni, за исключением того, что это совсем не работает для статического параметрического полиморфизма





Смоделировать концепции AsyncReadStream/AsyncWriteStream почти тривиально.
Единственная сложность связана с инициированием асинхронных операций с использованием шаблонов CompletionToken:
Например.:
#include <boost/asio.hpp>
#include <iostream>
#include <fmt/ranges.h>
namespace asio = boost::asio;
using namespace std::literals;
struct MockStream {
using error_code = boost::system::error_code;
using executor_type = asio::any_io_executor;
MockStream(asio::any_io_executor ex) : ex_(std::move(ex)) {}
executor_type get_executor() const { return ex_; }
template <typename Token> auto async_write_some(asio::const_buffer buf, Token&& token) {
return asio::async_initiate<Token, void(error_code, size_t)>( //
[fallback = ex_](auto h, auto buf) {
auto ex = asio::get_associated_executor(h, fallback);
asio::dispatch(ex, [=, h = std::move(h)]() mutable {
fmt::print("Simulated write of {} completed\n", buf.size());
std::move(h)({}, asio::buffer_size(buf));
});
},
token, buf);
}
template <typename Token> auto async_read_some(asio::mutable_buffer buf, Token&& token) {
return asio::async_initiate<Token, void(error_code, size_t)>( //
[fallback = ex_, ch = mock_data_++](auto h, auto buf) {
auto ex = asio::get_associated_executor(h, fallback);
std::fill(asio::buffers_begin(buf), asio::buffers_end(buf), ch);
asio::dispatch(ex, [=, h = std::move(h)]() mutable {
if (ch == 'e') {
auto partial = (rand() % buf.size()) / 4;
fmt::print("Simulated EOF with {} partial read\n", partial);
std::move(h)(asio::error::eof, partial);
} else {
fmt::print("Simulated read of {} completed\n", buf.size());
std::move(h)({}, asio::buffer_size(buf));
}
});
},
token, buf);
}
asio::any_io_executor ex_;
char mock_data_ = 'a';
};
int main() {
srand(time(NULL));
asio::io_context ioc;
MockStream stream(asio::system_executor{});
asio::async_write(stream, asio::buffer("Hello world"sv), [](auto ec, size_t n) {
fmt::print("Write completed with {} and {} bytes written\n", ec.message(), n);
});
std::array<char, 10> buf;
asio::async_read(stream, asio::buffer(buf), [&buf](auto ec, size_t n) {
fmt::print("Read completed with {} and {} bytes read: {}\n", ec.message(), n, buf);
});
asio::streambuf sbuf;
asio::async_read_until(stream, sbuf, "d", [&sbuf](auto ec, size_t n) {
fmt::print("Read-until completed with {} and {} bytes read\n", ec.message(), n);
// std::cout << "Streambuf: " << &sbuf << std::endl;
sbuf.consume(sbuf.size());
});
asio::async_read_until(stream, sbuf, "z", [&sbuf](auto ec, size_t n) {
fmt::print("Read-until completed with {} and {} bytes read\n", ec.message(), n);
std::cout << "Streambuf: " << &sbuf << std::endl;
});
ioc.run();
}
Печать, например.
Похожие реализации здесь:
Кроме того, рассмотрите возможность простого запуска локального соединения. Возможно, через домен UNIX или каналы. Если вы действительно считаете, что транспорт, буферизация и режимы отказа не имеют значения, просто полностью отключите уровень связи. в конце концов, это UNIT-тест, почему он зависит от реализации ввода-вывода?
Я не нахожу это тривиальным. Я пытался изменить ваш пример, чтобы он работал на меня. В двух чтениях, где он возвращает Success для вас, я получаю Undefined error.
Да, я имел в виду, что интерфейс тривиальный (3 функции). Поначалу асинхронные инициации не являются тривиальными. Можете ли вы поделиться тем, что у вас есть? Вы можете редактировать колиру.
Можно ли поделиться тем, что у меня есть, приватно?
Конечно. И, вау. Ты тот парень , который спрашивает об ошибке ? Это имеет больше смысла. Я обновил ответ, добавив демо-версию EOF.
Спасибо. Я очень близок к тому, чтобы это заработало, но я пытаюсь использовать io_service для своего варианта использования. Есть ли способ сделать это? Я получаю static assertion failed: AsyncReadStream type requirements not met.
Что ты имеешь в виду, @user129393192? io_service устарел, но не имеет ничего общего с концепцией потока. Если вы используете старые версии boost, обратитесь к документации вашей версии или обновления? [Я не думаю, что это «близко к тому, чтобы это работало», это все равно, что сказать, что ваш каяк близок к работе, когда он плывет вверх тормашками в потоке. Я предлагаю не слишком сильно идти против течения]
Я был уже довольно давно. Как я могу написать вам, чтобы поделиться тем, что у меня есть?
Моя реализация принимает io_service в качестве параметра для конструкции socket, но похоже, что она не примет это вместо any_io_executor. Интересно, есть ли какое-то внутреннее несоответствие версий. Я изо всех сил пытаюсь понять это и заставить это работать.
Несовпадение версий, окей. io_service уже давно устарел. Как я уже сказал, посмотрите документацию для вашей версии boost или обновите ее.
Я сделал. Я понял это. Спасибо за вашу помощь. Просто странно, что socket принимает io_service (там написано, что моя версия 1.73), тогда как в документации сказано, что она должна принимать только executor.
@ user129393192 В документах этого не сказано? boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/… (например, из boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/ip__tcp/…)
Обратите внимание, что даже 1.73.0. больше даже не документирует io_service, и он не будет компилироваться начиная с Boost 1.66(!!), если вы компилируете с BOOST_ASIO_NO_DEPRECATED. Это октябрь 2017 года.
Итак, помимо перемещения стоек ворот (вопрос задается о минимальном объекте потока, а не о замене подмножества данного типа сокета): минимальная версия, поддерживающая этот конструктор: coliru.stacked-crooked.com/a /4345f1eebcd60e7f
Вы можете использовать классическую инъекцию зависимостей и издеваться над своим интерфейсом. Чтобы быть более конкретным, вам нужно будет показать пример кода вашей системы и вызовов, которые вы пытаетесь имитировать. Но в принципе любую проблему такого рода можно жестоко решить, написав своего рода
class INetworkIface { abstract methods here };.