【问题标题】:EXPECT_CALL isn't satisfied, but test is passedEXPECT_CALL 不满足,但测试通过
【发布时间】:2021-06-07 15:12:08
【问题描述】:

我正在尝试测试对象的行为,但即使未调用模拟方法,我的测试也始终通过。

情况很简单。 io-blocks 很少。每个块都进行数据处理并将它们传递给下一个块。代码如下所示。

GtestCheck.h

#include <memory>
#include <string>

#include <gtest/gtest.h>
#include <gmock/gmock.h>

using ::testing::NiceMock;
using ::testing::_;
using ::testing::NotNull;
using ::testing::Exactly;

using namespace std::placeholders;

class IoBlock;
typedef std::shared_ptr<IoBlock> IoBlockSharedPtr;

enum class ModuleType : uint8_t
{
    None        =   0x00,
    Type0       =   0x01,
    Type1       =   0x02,
};

class IoBlock
{
    public:
        IoBlock() = delete;
        IoBlock(const IoBlock&) = delete;
        IoBlock(const IoBlock&&) = delete;
        virtual ~IoBlock();

    public:
        IoBlock(const std::string& config);

    public:
        void SetOutputObject(IoBlockSharedPtr outputModule);
        virtual void Input(int x, int y, int z) = 0;

    protected:
        void Output(int x, int y, int z);

    protected:
        ModuleType _moduleType = ModuleType::None;
        std::string _config = "";
        IoBlockSharedPtr _outputBlockPtr;
};


class IoType0 : public IoBlock
{
    public:
        IoType0() = delete;
        IoType0(const IoType0&) = delete;
        IoType0(const IoType0&&) = delete;
        virtual ~IoType0() override;

    public:
        IoType0(const std::string& config) : IoBlock(config) { }

    public:
        virtual void Input(int x, int y, int z) override final;
};


class IoType1mock : public IoBlock
{
    public:
        IoType1mock() = delete;
        IoType1mock(const IoType1mock&) = delete;
        IoType1mock(const IoType1mock&&) = delete;
        virtual ~IoType1mock() override;

    public:
        IoType1mock(const std::string& config);

    public:
        MOCK_METHOD(void, Input, (int x, int y, int z), (override, final));
};

class IoBlockTest : public ::testing::Test
{
    protected:
        void SetUp() override
        {
            _configString = "none0";
            _ioType0block = std::make_shared<IoType0>(_configString);
            _ioType1block = std::make_shared<NiceMock<IoType1mock>>(_configString);

            _ioType1block->SetOutputObject(_ioType0block);
            _ioType0block->SetOutputObject(_ioType1block);

            // gtest reports shared_ptr memory leak
            ::testing::Mock::AllowLeak(_ioType1block.get());
        }

        virtual void TearDown() override { }

        static void SetUpTestCase() { }

        static void TearDownTestCase() { }

    public:
        std::string _configString = "none";
        std::shared_ptr<IoType0> _ioType0block = nullptr;
        std::shared_ptr<NiceMock<IoType1mock>> _ioType1block = nullptr;
};

GtestCheck.cpp

#include "GtestCheck.h"

IoBlock::IoBlock(const std::string& config)
    : _config(config) {}

void IoBlock::SetOutputObject(IoBlockSharedPtr outputModule)
{
    _outputBlockPtr = outputModule;
}

void IoBlock::Output(int x, int y, int z)
{
    if ( _outputBlockPtr != nullptr )
        _outputBlockPtr->Input(x, y, z);
}

IoBlock::~IoBlock() {}

IoType0::~IoType0() {}

IoType1mock::~IoType1mock() {}

void IoType0::Input(int x, int y, int z)
{
    static size_t callCounter = 0;

    if (callCounter++ == 5)
        Output(x, y, z);
}

IoType1mock::IoType1mock(const std::string& config)
    : IoBlock(config) { }

main.cpp

#include <iostream>

#include "GtestCheck.h"

TEST_F(IoBlockTest, SimpleRoute)
{
    //EXPECT_CALL(*_ioType1block, Input(_, _, _)).Times(1);

    EXPECT_CALL(*_ioType1block, Input(_, _, _))
            .Times(1)
            .WillOnce([](int x, int y, int z)
            {
                std::cout << "Input: " << x
                          << " " << y
                          << " " << z << std::endl;
            });

    // Input method trigger code
    //for (size_t i = 0;i < 6; ++i)
    //    _ioType0block->Input(0, 1, 2);
}

int main(int argc, char *argv[])
{
    std::cout << "IoBlockTest unit-tests have been running" << std::endl;
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

当我运行测试时,它通过了。当我取消注释 Input method trigger 行时,它也通过了。

我试图调查这种行为,但没有成功。当我评论那一行时,我只注意到一件事:

_ioType0block->SetOutputObject(_ioType1block);

我的测试如我预期的那样失败了。

如果有人知道这种行为的原因,请您描述一下。 提前致谢。

测试环境:

操作系统5.10.34-1-MANJARO

gtest1.10.0

编译器g++ (10.2.0)

【问题讨论】:

    标签: c++ unit-testing googletest


    【解决方案1】:

    提示在这里:

    // gtest reports shared_ptr memory leak
    ::testing::Mock::AllowLeak(_ioType1block.get());
    

    您的模拟对象泄漏,因此它永远不会被破坏,因此模拟调用的数量永远不会与预期的 1 进行比较。

    由于循环依赖关系而发生泄漏。您必须借助子级中的std::weak_ptr 或在子级中使用std::unique_ptr 和原始指针而不是std::shared_ptr 来打破这些依赖关系。

    【讨论】:

    • 感谢您的回答。但我试图测试的有点不同。我想检查块 0 和块 1 之间的通信。对于 block-0 我有一个类,对于 block-1 没有,因此我创建了一个模拟对象。测试应该捕获间接调用 _ioType1block->Input 方法。我知道可以为纯虚拟方法创建模拟。
    • 您选择的名称令人困惑。 IoType1mock 从它的名字说它嘲笑IoType1,但根据你的评论它嘲笑IoBlock
    • 是的,它是IoBlock 的模拟。可能名称不合适,但我尝试通过该命名分隔 2 个不同的类。与主要问题相关,您有什么想法为什么EXPECT_CALL 总是很满意?
    • 感谢您的解释。这是一个伟大的收获!
    猜你喜欢
    • 2016-04-13
    • 1970-01-01
    • 2020-06-22
    • 2018-04-23
    • 2017-12-18
    • 2020-01-01
    • 2022-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多