GoogleTest EXPECT_CALL 抛出无趣的调用警告和 returns 0 次调用

GoogleTest EXPECT_CALL throws uninteresting call warning and returns 0 calls

我正在尝试模拟一个外部库并测试从我的接口调用了哪些库的 API,我遇到了一种我似乎无法理解的行为(第一次使用模拟)。所以,我有一个像这样的模拟 class:

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来处理值:

class VulkanBaseTest : public ::testing::Test {
public:
    VulkanBaseTest() {
        vulkanLibMock = new ::testing::NiceMock<VulkanLibMock>;
    }
    
    ~VulkanBaseTest() {
        delete vulkanLibMock;
        vulkanLibMock = nullptr;
    }
    
public:
    static VulkanLibMock *vulkanLibMock;
};

所以,我从中继承了一个测试class:

class VulkanDescriptorManagerTests : public VulkanBaseTest {
public:
    VulkanDescriptorManagerTests() = default;
};

并添加了一个测试用例:

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

我在这里错过了什么?我知道调用了该函数,因为可以在 ON_CALL 实现中看到 cout 语句的输出。为什么EXPECT_CALL认为函数没有被调用?

编辑: VulkanDescriptorManager(简化):

VulkanDescriptorManager(VkDevice device) {
  // global library functions
  VkDescriptorSetLayoutCreateInfo sceneCreateInfo{};
  vkCreateDescriptorSetLayout(device, &createInfo, nullptr, &this->sceneDescriptorSet);

  VkDescriptorSetLayoutCreateInfo materailCreateInfo{};
  vkCreateDescriptorSetLayout(device, &materailCreateInfo, nullptr, &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);
}

根据 gmock doc

Important note: gMock requires expectations to be set before the mock functions are called, otherwise the behavior is undefined. Do not alternate between calls to EXPECT_CALL() and calls to the mock functions, and do not set any expectations on a mock after passing the mock to an API.

您的代码需要调整这两行的顺序:

VulkanDescriptorManager manager(nullptr);    
EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout);

至:

EXPECT_CALL(*vulkanLibMock, vkCreateDescriptorSetLayout);
VulkanDescriptorManager manager(nullptr);    

文档也解释了为什么他们使用这样的设计:

Why does gMock work like that? Well, specifying the expectation beforehand allows gMock to report a violation as soon as it rises, when the context (stack trace, etc) is still available. This makes debugging much easier.