轻松编写保持状态的模拟
Easily writing mocks that hold state
我正在测试一些大量使用回调的代码,它看起来像这样:
class Client {
public:
Client(Socket socket) {
socket->onA([this] {
// call private Client methods....
});
socket->onB([this] {
// call private Client methods...
});
// repeated for onC, onD, and onE
}
void Start();
void Stop();
private:
// and almost all of the state is private
};
与外界的通信几乎完全是通过Socket
提供的接口,长这样:
class Socket {
public:
void onA(std::function<void()> callBack) = 0;
void onB(std::function<void()> callBack) = 0;
void onC(std::function<void()> callBack) = 0;
void onD(std::function<void()> callBack) = 0;
void onE(std::function<void()> callBack) = 0;
// various other public methods
};
重要的是我既可以验证 onX
函数已被调用,又可以访问传递给它们的参数(因为 Client
的状态会随着来自 Socket
的通知,我需要在我的测试中模拟这些通知。
我可以编写一个如下所示的模拟:
class MockSocket : public Socket {
public:
MOCK_METHOD1(onA, void(std::function<void()> callBack));
MOCK_METHOD1(onB, void(std::function<void()> callBack));
// etc
};
但我需要能够写出以下形式的期望:"expect that this MockSocket
object had its onA
method called once, now call the function that was passed as an argument to onA
. Now check that the write
method of Socket
was called with this string:...."
在另一种情况下,当我只有一个这种功能时,我做了类似的事情:
class MockSocket : public Socket {
public:
// as before
void setCallbackForA(std::function<void()> callBack) {
callbackForA = callBack;
}
void callCallbackForA() {
callbackForA();
}
// etc
private:
std::function<void()> callbackForA;
std::function<void()> callbackForB;
// etc
};
但是要完成这项工作,样板文件的数量将是荒谬的(5 个 getter 和 5 个 setter + 所有 EXPECT_CALL
s,从测试用例到测试用例会非常轻微),我怀疑必须有更好的方法来做到这一点(无需求助于 C 风格的宏)。是否有一些模板魔术可以提供帮助?有什么可以让它更容易处理的吗?
我假设您正在测试 Client
class - 所以您真正需要的是存储 Client
在 Socket
中注册的回调函数。通过 SaveArg<N>
行动做到这一点。见代码:
class ClientTest : public ::testing::Test
{
public:
std::function<void()> onACallback;
std::function<void()> onBCallback;
//...
MockSocket mockSocket;
std::unique_ptr<Client> objectUnderTest;
void SetUp() override
{
using ::testing::SaveArg;
EXPECT_CALL(mockSocket, onA(_)).WillOnce(SaveArg<0>(&onACallback));
EXPECT_CALL(mockSocket, onB(_)).WillOnce(SaveArg<0>(&onBCallback));
//...
objectUnderTest = std::make_unique<Client>(mockSocket);
}
};
通过上面的测试 class - 您可以收集所有回调。您可以使用这些捕获的回调在您的客户端 class 中触发操作,例如下面的测试用例:
TEST_F(ClientTest, shallDoThisAndThatOnA)
{
// here you put some expectations
//...
onACallback(); // here you trigger your Client object under test
// now you make some assertions (post actions)
//...
}
我正在测试一些大量使用回调的代码,它看起来像这样:
class Client {
public:
Client(Socket socket) {
socket->onA([this] {
// call private Client methods....
});
socket->onB([this] {
// call private Client methods...
});
// repeated for onC, onD, and onE
}
void Start();
void Stop();
private:
// and almost all of the state is private
};
与外界的通信几乎完全是通过Socket
提供的接口,长这样:
class Socket {
public:
void onA(std::function<void()> callBack) = 0;
void onB(std::function<void()> callBack) = 0;
void onC(std::function<void()> callBack) = 0;
void onD(std::function<void()> callBack) = 0;
void onE(std::function<void()> callBack) = 0;
// various other public methods
};
重要的是我既可以验证 onX
函数已被调用,又可以访问传递给它们的参数(因为 Client
的状态会随着来自 Socket
的通知,我需要在我的测试中模拟这些通知。
我可以编写一个如下所示的模拟:
class MockSocket : public Socket {
public:
MOCK_METHOD1(onA, void(std::function<void()> callBack));
MOCK_METHOD1(onB, void(std::function<void()> callBack));
// etc
};
但我需要能够写出以下形式的期望:"expect that this MockSocket
object had its onA
method called once, now call the function that was passed as an argument to onA
. Now check that the write
method of Socket
was called with this string:...."
在另一种情况下,当我只有一个这种功能时,我做了类似的事情:
class MockSocket : public Socket {
public:
// as before
void setCallbackForA(std::function<void()> callBack) {
callbackForA = callBack;
}
void callCallbackForA() {
callbackForA();
}
// etc
private:
std::function<void()> callbackForA;
std::function<void()> callbackForB;
// etc
};
但是要完成这项工作,样板文件的数量将是荒谬的(5 个 getter 和 5 个 setter + 所有 EXPECT_CALL
s,从测试用例到测试用例会非常轻微),我怀疑必须有更好的方法来做到这一点(无需求助于 C 风格的宏)。是否有一些模板魔术可以提供帮助?有什么可以让它更容易处理的吗?
我假设您正在测试 Client
class - 所以您真正需要的是存储 Client
在 Socket
中注册的回调函数。通过 SaveArg<N>
行动做到这一点。见代码:
class ClientTest : public ::testing::Test
{
public:
std::function<void()> onACallback;
std::function<void()> onBCallback;
//...
MockSocket mockSocket;
std::unique_ptr<Client> objectUnderTest;
void SetUp() override
{
using ::testing::SaveArg;
EXPECT_CALL(mockSocket, onA(_)).WillOnce(SaveArg<0>(&onACallback));
EXPECT_CALL(mockSocket, onB(_)).WillOnce(SaveArg<0>(&onBCallback));
//...
objectUnderTest = std::make_unique<Client>(mockSocket);
}
};
通过上面的测试 class - 您可以收集所有回调。您可以使用这些捕获的回调在您的客户端 class 中触发操作,例如下面的测试用例:
TEST_F(ClientTest, shallDoThisAndThatOnA)
{
// here you put some expectations
//...
onACallback(); // here you trigger your Client object under test
// now you make some assertions (post actions)
//...
}