3

I'm using gmock and have mocked a function that takes boost::beast::http::response_parser as an out parameter. Function signature looks something like:

error_code readFromSocket(boost::beast::http::response_parser& resp);

Now, to test this function, I mocked and use EXPECT_CALL like:

boost::beast::http::response_parser respObject;
// set status code in respObject
EXPECT_CALL(mockObject, readFromSocket(_))
    .Times(1)
    .DoAll(SetArgReferee<0>(respObject), Return(err::success));

And it returns me an operator= deleted compilation error. I have tried various combinations using ByRef, ByMove, using std::ref but none of them works and I understand why they don't work.

Has anyone ever ran into a similar situation before and know how to solve this error? Please let me know if you need clarification.

Thank you.

EDIT

This is what readFromSocket looks like:

template<typename T>
error_code readFromSocket(beast::http::response_parser<T>& resp,
                          boost::asio::yield_context yield)
{
    // read from socket using beast::http::async_read
    return success;
}

This is how I call it.

beast::http::response_parser<beast::http::string_body> resp;
resp.body_limit((std::numeric_limits<std::string::size_type>::max)());
auto ec = readFromSocket(resp, yield);

// after this I do error handling and response status code handling.
Quarra
  • 2,527
  • 1
  • 19
  • 27
Abhishek Arya
  • 450
  • 4
  • 16
  • Wait. Are you unit testing the _implementation details_ of Boost Beast? I can _almost_ see that you might use `beast::http::message<...>` as dictionary types in your transport layer, but the parsers seem (very!) off-topic (unless you're testing Boost Beast internally, but I would be surprised if you chose runtime polymorphic mocking in that case) – sehe Jul 27 '20 at 19:47
  • No, I'm not testing response_parser. I'm testing one or the classes that I wrote for performing socket operations. In there, I use response_parser object to read from my socket. Specifically, I call async_read function. And in my case, I found response_parser to be more appropriate than using beast::http::message class. – Abhishek Arya Jul 27 '20 at 20:05
  • Sure. E.g. if you do streaming responses (e.g.) it does make more sense. However, that doesn't immediately imply your interface-under-test exposes these building blocks. Consider decoupling the interface a little and passing only the message being parsed *or* test at the level of the composed operations. I venture that if you want to test below that level, you should be testing building blocks like `handler_ptr`, `bind_front_handler` etc. Separate from the library/network realities, testing against mocks tells you nothing useful. – sehe Jul 27 '20 at 20:15
  • Makes sense. I will give it a thought and think about how can I decouple my interface. Thanks. – Abhishek Arya Jul 27 '20 at 20:33
  • I need to set body_limit for my response for which I need to call resp.body_limit() which isn't available in beast::http::message. So, I can't rely get away with using response_parser. What do you mea by saying `test at the level of the composed operations`? Do you mean testing while writing to actual socket? If yes, I am already doing it. – Abhishek Arya Jul 27 '20 at 21:56
  • What exactly do you want to test? Call some methods on `response_parser`? Store the parser and check something else? – local-ninja Jul 27 '20 at 21:59
  • @AbhishekArya Higher level: testing that the body limit is functioning, not sensing that a given limit has been set. After all, we don't really need a unit test to ensure that `body_limit()` does what it says on the tin. We need to be sure that the effect is limiting memory and inbound traffic resources. If you need to sense the limit being set, make it a testable interface to read the number that will be set. Keep everything simple. – sehe Jul 27 '20 at 22:02
  • @sehe, No, I don't want test whether `body_limit()` works fine. Editing question to reflect what readFromSocket does. – Abhishek Arya Jul 27 '20 at 22:29

1 Answers1

0

I was trying to achieve the same behaviour and WithArg was the key. This is an example of what I ended up with:

#include <gmock/gmock.h>
#include <gtest/gtest.h>


using namespace testing;

struct NonCopyable {
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;

    int a;
};

struct WorkWithNonCopyable {
    virtual ~WorkWithNonCopyable() = default;

    virtual bool modify_non_copyable(NonCopyable& nc) = 0;
};

struct WorkWithNonCopyableMock {
    MOCK_METHOD1(modify_non_copyable, bool(NonCopyable& nc));
};

TEST(WorkWithNonCopyableTest, NormalizedRoiDetectorTest) {
    WorkWithNonCopyableMock work_with_non_copyable_mock{};

    EXPECT_CALL(work_with_non_copyable_mock, modify_non_copyable(_))
        .WillOnce(DoAll(WithArg<0>([](auto& arg) { arg.a = 42; }), Return(true)));

    NonCopyable non_copyable;
    ASSERT_TRUE(work_with_non_copyable_mock.modify_non_copyable(non_copyable));
    ASSERT_EQ(non_copyable.a, 42);
}
Quarra
  • 2,527
  • 1
  • 19
  • 27