我在班级设计中遵循了 Herb Sutter 的 NVI idiom,因为它有好处。我的抽象类简而言之如下。
class Communicator {
public:
bool Connect(void) {
// some preoperations
return DoConnect();
}
// 3 more similar public member functions
private:
virtual bool DoConnect(void) = 0
// 3 more similar pure virtual member functions
}
我将 Communicator 类注入到我想要测试的类中,如下所示(我从Google docs找到了这个方法):
template <typename CommunicatorClass> // There will be some constraints
class TestingClass {
public:
TestingClass(std::unique_ptr<CommunicatorClass> comm) : comm_{std::move(comm)} { }
private:
std::unique_ptr<CommunicatorClass> comm_ = nullptr;
}
我的模拟课:
class MockCommunicator : public Communicator {
public:
MOCK_METHOD(bool, Connect, ());
// 3 more mocks
}
我的测试套件设置:
// some code ..
auto mock = std::make_unique<MockCommunicator>();
TestingClass<MockCommunicator>(std::move(mock));
你可以猜到,因为我没有重写 4 个私有虚函数,所以 MockCommunicator 变得抽象,并且编译器给出错误。我想到的选项:
有什么推荐的方法来处理这种情况吗?
注意:我使用 C++20
与其嘲笑你的公共
Connect
,不如嘲笑虚拟私有DoConnect
(当然,以及所有其他纯虚函数,它并不难看,如果你嘲笑,你会嘲笑所有接口,而不是其中的一小部分) ):
class MockCommunicator : public Communicator {
public:
MOCK_METHOD(bool, DoConnect, (), (override));
// 3 more mocks
};
请注意,在模拟中
DoConnect
是公共的,因此您可以在 EXPECT_CALL
宏中使用它,但它不会破坏您的界面,在使用指向基类的指针的 TestingClass
中,它们仍然具有私有可见性。然后假设您的 TestingClass
有一些简单的方法来调用 Communicator
可观察行为:
bool run() {
return comm_->Connect();
}
你可以写一个简单的测试:
TEST(xxx, yyy) {
auto mock = std::make_unique<MockCommunicator>();
auto* rawMock = mock.get();
auto sut = TestingClass<MockCommunicator>(std::move(mock));
EXPECT_CALL(*rawMock, DoConnect()).WillRepeatedly(testing::Return(true));
ASSERT_TRUE(sut.run());
}
有了这个,你甚至不需要你的
TestingClass
成为模板:
class TestingClass {
public:
TestingClass(std::unique_ptr<Communicator> comm) : comm_{std::move(comm)} { }
bool run() {
return comm_->Connect();
}
private:
std::unique_ptr<Communicator> comm_;
};
顺便说一句,你的
class Communicator
必须有一个虚拟析构函数。