#c #googletest
Вопрос:
Я пытаюсь издеваться над внешней библиотекой и проверять, какие API библиотеки вызываются из моих интерфейсов, и я столкнулся с поведением (впервые использующим издевательство), которое я, похоже, не могу понять. Итак, у меня есть такой макет класса:
class VulkanLibMock {
public:
MOCK_METHOD(VkResult, vkCreateDescriptorSetLayout, (VkDevice, const VkDescriptorSetLayoutCreateInfo *, const VkAllocationCallbacks *, VkDescriptorSetLayout *pSetLayout));
MOCK_METHOD(VkResult, vkCreateDescriptorPool, (VkDevice, const VkDescriptorPoolCreateInfo *, const VkAllocationCallbacks *, VkDescriptorPool *));
};
и тестовый базовый класс для обработки значений:
class VulkanBaseTest : public ::testing::Test {
public:
VulkanBaseTest() {
vulkanLibMock = new ::testing::NiceMock<VulkanLibMock>;
}
~VulkanBaseTest() {
delete vulkanLibMock;
vulkanLibMock = nullptr;
}
public:
static VulkanLibMock *vulkanLibMock;
};
Итак, я унаследовал от него тестовый класс:
class VulkanDescriptorManagerTests : public VulkanBaseTest {
public:
VulkanDescriptorManagerTests() = defau<
};
и добавил один тестовый пример:
TEST_F(VulkanDescriptorManagerTests, MyTest) {
ON_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout).WillByDefault([](VkDevice, const VkDescriptorSetLayoutCreateInfo *, const VkAllocationCallbacks *, VkDescriptorSetLayout *) {
std::cout << "Called: vkCreateDescriptorSetLayout";
return VK_SUCCESS;
});
ON_CALL(*vulkanLibMock, vkCreateDescriptorPool).WillByDefault([](VkDevice, const VkDescriptorPoolCreateInfo *, const VkAllocationCallbacks *, VkDescriptorPool *) {
std::cout << "Called: vkCreateDescriptorPool";
return VK_SUCCESS;
});
// this is the class that I am trying to test
VulkanDescriptorManager manager(nullptr);
EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout);
}
Если я не использую NiceMock
, я получаю следующее предупреждение при запуске тестов
Called: vkCreateDescriptorSetLayout <-- cout in `ON_CALL`
Uninteresting mock function call - taking default action specified at:
VulkanDescriptorManager.test.cpp:12:
Function call: vkCreateDescriptorSetLayout(NULL, 0x7ffeefbfec50, NULL, 0x7ffeefbfedf0)
Returns: 0
NOTE: You can safely ignore the above warning unless this call should not happen. Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call. See https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md#knowing-when-to-expect for details.
Called: vkCreateDescriptorSetLayout <-- cout in `ON_CALL`
GMOCK WARNING:
Uninteresting mock function call - taking default action specified at:
VulkanDescriptorManager.test.cpp:12:
Function call: vkCreateDescriptorSetLayout(NULL, 0x7ffeefbfec20, NULL, 0x7ffeefbfedf8)
Returns: 0
NOTE: You can safely ignore the above warning unless this call should not happen. Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call. See https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md#knowing-when-to-expect for details.
Called: vkCreateDescriptorPool <-- cout in `ON_CALL`
GMOCK WARNING:
Uninteresting mock function call - taking default action specified at:
VulkanDescriptorManager.test.cpp:17:
Function call: vkCreateDescriptorPool(NULL, 0x7ffeefbfeca0, NULL, 0x7ffeefbfee00)
Returns: 0
NOTE: You can safely ignore the above warning unless this call should not happen. Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call. See https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md#knowing-when-to-expect for details.
И я получаю ошибку независимо от использования NiceMock:
Actual function call count doesn't match EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout)...
Expected: to be called once
Actual: never called - unsatisfied and active
Что я здесь упускаю? Я знаю, что функция вызывается, так как могу видеть вывод cout
инструкции в ON_CALL
реализации. Почему EXPECT_CALL
думает, что функция не вызывается?
ИЗМЕНИТЬ: VulkanDescriptorManager (упрощенно):
VulkanDescriptorManager(VkDevice device) {
// global library functions
VkDescriptorSetLayoutCreateInfo sceneCreateInfo{};
vkCreateDescriptorSetLayout(device, amp;createInfo, nullptr, amp;this->sceneDescriptorSet);
VkDescriptorSetLayoutCreateInfo materailCreateInfo{};
vkCreateDescriptorSetLayout(device, amp;materailCreateInfo, nullptr, amp;this->materialDescriptorSet);
// same logic for vkCreateDescriptorPool
}
Вызываемые функции являются глобальными и исходили из библиотеки. Чтобы иметь возможность протестировать их, я разорвал связь с библиотекой и создал свою собственную реализацию для функций:
// this is in my global library mock file:
VkResult vkCreateDescriptorSetLayout(
VkDevice device, const VkDescriptorSetLayoutCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDescriptorSetLayout *pSetLayout) {
// Calls the function in mock that is accessible from
// VulkanBaseTest's static variable
return VulkanBaseTest::vulkanLibMock->vkCreateDescriptorSetLayout(device, pCreateInfo, pAllocator, pSetLayout);
}
VkResult vkCreateDescriptorPool(VkDevice device,
const VkDescriptorPoolCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDescriptorPool *pDescriptorPool) {
return VulkanTestFixture::vulkanLibMock->vkCreateDescriptorPool(device, pCreateInfo, pAllocator, pDescriptorPool);
}
Комментарии:
1. Что такое VulkanDescriptorManager и как он связан с vulkanLibMock?
2.
EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout);
компилируется ли это без ожидаемых параметров или заполнителей?3. Похоже, нам нужно скорректировать последовательность этих двух утверждений:
VulkanDescriptorManager manager(nullptr); EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout);
4. Я обновил свой пост упрощенной версией VulkanDescriptorSet (в основном перенес всю логику из внутренних частных функций в конструктор).
5. Где ваш полный тест? Как правило, нам нужно поставить
EXPECT_CALL
перед фактическим телом нашего теста.
Ответ №1:
Согласно документу gmock
Важное примечание: gMock требует, чтобы ожидания были установлены до вызова фиктивных функций, в противном случае поведение не определено. Не чередуйте вызовы функции EXPECT_CALL() и вызовы функций макета и не устанавливайте никаких ожиданий для макета после передачи макета API.
Вашему коду необходимо настроить последовательность этих двух строк с:
VulkanDescriptorManager manager(nullptr);
EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout);
Для:
EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout);
VulkanDescriptorManager manager(nullptr);
Этот документ также объяснил, почему они используют такой дизайн:
Почему gMock так работает? Ну, если заранее указать ожидание, gMock сообщит о нарушении, как только оно возникнет, когда контекст (трассировка стека и т. Д.) Все еще Доступен. Это значительно облегчает отладку.