GoogleTest EXPECT_CALL выдает предупреждение о неинтересном вызове и возвращает 0 вызовов

#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 сообщит о нарушении, как только оно возникнет, когда контекст (трассировка стека и т. Д.) Все еще Доступен. Это значительно облегчает отладку.