#c #googlemock
Вопрос:
Я новичок в Google testing framework и искал решение этого вопроса на SO, но не смог найти никаких решений в отношении C . В любом случае, вот что я пытаюсь сделать. У меня есть государственная машина(служба), которая вызывается внутри клиентского кода.
//IStateMachine.h class IStateMachine { public: bool Run(const std::stringamp; action) = 0; bool IsTxnValid(const std::stringamp; action)= 0; } //StateMachine.h class StateMachine : public IStateMachine { bool Run(const std::stringamp; action) override; bool IsTxnValid(const std::stringamp; action) override; } //StateMachine.cpp bool StateMachine::IsTxnValid(const std::stringamp; action) { //Checks whether the given action is valid for the given state. } bool StateMachine::Run(const std::stringamp; action) { if(IsTxnValid(action)) // #E { //Do processing return true; } return false; } //Client.h contains a class Client which has function called RunService. Client { public: void RunService(); std::unique_ptrlt;IStateMachinegt; service_; // Initialised to a non null value in either ctr or // factory. } //Client.cpp bool Client::RunService(std::stringamp;action) { if(!service_-gt;Run(action)) //Run in turn calls IsTxnValid(). { return false; } return true; }
Сейчас я пишу тестовый пример для проверки функционирования RunService
. Я ожидаю, что если Client::IsTxnValid(param)
возвращает false, то так и должно RunService
быть .
Я успешно настроил рецепт тестирования и смог запустить основные тесты. Вот соответствующий тест, который я написал. При запуске этого теста я получаю ошибку, которая IsTransitionValid
никогда не вызывается.
TEST_F(ClientTest, RunService) { EXPECT_CALL(*p_service, Run("some_action")); // #A // EXPECT_CALL(*p_service, Run(testing::_)).WillOnce(::testing::Return(true)); //#B EXPECT_CALL(*p_service,IsTransitionValid(testing::_)).WillOnce(::testing::Return(false)); //#C : This never gets called. EXPECT_EQ(false, x_client-gt;RunService()); }
Как мне правильно позвонить IsTransitionValid
?
Комментарии:
1. Не забудьте добавить виртуальный dtor в
IStateMachine
Ответ №1:
Вам не нужно устанавливать это ожидание. Я бы пошел еще дальше: вы даже не должны зависеть от реализации Run
in IStateMachine
: вас должно волновать только то, какие входные данные он предоставляет (параметры, проверенные с помощью сопоставителей) и какие выходные данные он может возвращать (так что в основном только контракт между этими двумя классами), и в этом вся прелесть!
Это деталь реализации StateMachine
класса (реальная реализация), что делается при Run
вызове. Единственное, что вам нужно проверить в своем тесте, — это действовать в соответствии с результатом Run
. Использование правила тройного А (упорядочить, действовать, утверждать): вы упорядочиваете условия тестового случая (используя EXPECT_CALL
s), затем действуете (вызываете RunService
), а затем утверждаете (проверяете результат RunService
).
Технические детали: Когда вы создаете макет, унаследовав от class Foo
:
class Foo { public: virtual ~Foo() = default; virtual void bar() = 0; }
Путем определения:
class FooMock : public Foo { MOCK_METHOD0( bar, void()); }
gmock добавит bar
(метод для переопределения) и gmock_bar
(внутренние сведения о gmock) методы в FooMock
класс. bar
в данном случае имеет пустую реализацию. FooImpl
и FooMock
разделяют интерфейс, но имеют разные реализации — следовательно, в нем не выполняется вызов IsTxnValid
Run
: макет класса просто не знает (и не заботится) о том, как Run
он реализован StateMachine
. Помните: в своем тестовом наборе вы взаимодействуете с StateMachineMock
ним, и вас интересует только взаимодействие с его общедоступным интерфейсом, контракт между этими двумя классами и то, как они взаимодействуют друг с другом.
Тем не менее, вам, конечно, нужно протестировать StateMachine
класс. Это может зависеть от еще одного интерфейса в его реализациях: он будет протестирован с другим набором насмешек. Но Client
не должен знать об этом.