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.
我正在尝试模拟一个外部库并测试从我的接口调用了哪些库的 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.