У меня есть класс, который вызывает издевательскую функцию в списке инициализаторов. Я хочу использовать EXPECT_CALL, чтобы убедиться, что фиктивная функция вызывается только один раз. Проблема в том, что я не могу использовать макрос перед конструктором, потому что это первая функция, которая запускается, ни после нее, потому что в конструкторе вызывается издевательская функция.
Например: ui.cpp
class UI {
public:
UI() = default;
~UI() = default;
virtual std::string get_name() {
std::string name;
std::cin >> name;
return name;
}
};
foo.cpp
class Foo {
public:
Foo(UI& ui) : m_name(ui.get_name()) {}
~Foo() = default;
};
mock_ui.hpp
class MockUI : public UI {
MockUI() : UI() = default;
~MockUI() = default;
MOCK_METHOD(std::string, get_name, (), (override));
};
Проблема возникает здесь: foo_test.cpp
class FooTest : ::testing::Test {
public:
// I want to call EXPECT_CALL(m_ui, get_name()) before this line executes.
FooTest() : m_foo(MockUI()) {}
~FooTest() = default;
protected:
void SetUp() override {}
void TearDown() override {}
Foo m_foo;
MockUI m_ui;
};
Я попытался инициализировать объект Foo
в функции SetUp()
, но у Foo нет конструктора по умолчанию, поэтому его нужно инициализировать в конструкторе FooTest
.
Решение?
Единственная идея, которая у меня есть, это вызвать EXPECT_CALL()
в конструкторе MockUI следующим образом:
mock_ui.hpp
class MockUI : public UI {
MockUI() : UI() {
EXPECT_CALL(*this, get_name());
}
~MockUI() = default;
MOCK_METHOD(std::string, get_name, (), (override);
};
Проблема в том, что я могу использовать MockUI, не вызывая get_name()
или вызывая его несколько раз, но это лучшее решение, которое у меня есть.
Любые другие предложения?
Можете ли вы предоставить MCVE? Вот отправная точка Godbolt. Что такое тестируемый код?
Также я вижу, что вы вводите зависимость неправильно (передаете по значению).
Вы можете отложить инициализацию Foo
с помощью указателя:
class FooTest : ::testing::Test {
public:
FooTest()
{
EXPECT_CALL(m_ui, get_name());
m_foo = std::make_unique<Foo>(m_ui);
}
protected:
std::unique_ptr<Foo> m_foo;
MockUI m_ui;
};
Или добавив родительский класс, который сначала инициализирует MockUI
:
class FooTestHelper: public ::testing::Test
{
public:
FooTestHelper() { EXPECT_CALL(m_ui, get_name()); }
protected:
MockUI m_ui;
};
class FooTest : public FooTestHelper
{
public:
FooTest(): FooTestHelper{}, m_foo{m_ui} {}
private:
Foo m_foo;
};
Вы также можете добавить EXPECT_CALL в инициализацию фиктивной переменной:
class FooTest : ::testing::Test {
public:
FooTest() {}
~FooTest() = default;
protected:
void SetUp() override {}
void TearDown() override {}
MockUI m_ui;
bool dummy = (EXPECT_CALL(m_ui, get_name()), true);
Foo m_foo{m_ui};
};
Я переместил инициализацию m_foo
, но просто для большей наглядности порядок инициализации можно оставить в конструкторе.
Это хорошее решение! Также вы можете использовать манекен ожидания = EXPECT_CALL(), а затем проверить, вызывается ли он перед другими функциями с помощью After().
Несмотря на возможность
EXPECT_CALL
в конструкторе, показанный код плохо спроектирован. Объект не должен заботиться о том, вызываются ли его методы.