如何使用Gmock检查基类功能



这与如何使用gmock测试一类称之为基类方法的方法非常密切

我正在使用gtest和gmock测试新功能,所以我有一个基类...

class SimpleObject
{
public:
    explicit SimpleObject() {}
    virtual void moveX(int dX)
    {
        // Do important stuff like updating position, bounding box etc.
    }
    // ...
};

基于其他TDD我有一个派生类,新功能是在我在派生对象上调用movex时,它将做特定的事情,但是它也需要在simpleObject中执行重要的事情:: movex。p>我已经具有与SimpleObject :: Movex函数相关的测试驱动的单元测试,因此我不想重复它们的派生类。只要我知道simpleobject :: movex被称为hovex,那么一切都是笨拙的。

无论如何,基于上面的链接,然后遵循TDD,我最终得到了以下内容。

派生类:

    class ComplexObject : public SimpleObject
    {
    public:
        virtual void moveX(int dX)
        {
            // Do something specific
        }
    };

"可测试"类:

class TestableComplexObject : public ComplexObject
{
public:
    MOCK_METHOD1(moveX, void(int dX));
    void doMoveX(int dX)
    {
        SimpleObject::moveX(dX);
    }
};

测试:

TEST_F(ATestableComplexObject, CallsBaseClassMoveXWhenMoveXIsCalled)
{
    int dX(8);
    TestableComplexObject obj;
    EXPECT_CALL(obj, moveX(dX))
                .Times(1)
                .WillRepeatedly(testing::Invoke(&obj, &TestableComplexObject::doMoveX));
    obj.moveX(dX);
}

如果我运行测试,那么一切都会通过。这是不正确的,因为您可以看到ComplexObject :: Movex无需做任何事情。

也,无论我在Domovex中放置什么(我认为是为了设置我的期望),测试仍然会通过。

我显然在这里缺少一些简单的东西,所以有什么想法吗?

感谢您的评论,对设计进行了调整,我可以测试我想要的内容。

首先,为SimpleObject创建一个接口:

class ISimpleObject
{
public:
    virtual void moveX(int dX) = 0;
};

我的简单对象类,然后实现此处:

class SimpleObject : public ISimpleObject
{
public:
    explicit SimpleObject() {}
    virtual void moveX(int dX)
    {
        (void) dX;
        // Do important stuff like updating position, bounding box etc.
    }
};

它不是从SimpleObject继承而来的复杂对象,而是从界面继承并拥有一个简单对象(即它具有'而不是"是")。构造函数确保我们通过一个简单的对象,而这使模拟更容易。

class ComplexObject : public ISimpleObject
{
public:
    ComplexObject(SimpleObject *obj)
    {
        _obj = obj;
    }
    virtual void moveX(int dX)
    {
        _obj->moveX(dX);
    }
private:
    SimpleObject *_obj;
};

现在,我只是模拟我从SimpleObject感兴趣的电话

class SimpleObjectMock : public SimpleObject
{
public:
        MOCK_METHOD1(moveX, void(int dX));
        // Do important stuff like updating position, bounding box etc.
};

测试也简化了

TEST_F(AComplexObject, CallsBaseClassMoveXWhenMoveX)
{
    int dX(8);
    SimpleObjectMock mock;
    ComplexObject obj(&mock);
    EXPECT_CALL(mock, moveX(dX)).Times(1);
    obj.moveX(dX);
}

行为是预期的。如果ConspectObject :: Movex函数为空(就像开始时一样),则测试失败。它只有在您调用SimpleObject :: Movex。

时才通过

您需要以检查是否可能调用SimpleObject::moveX的方式来设计ComplexObject。做到这一点的一种方法:用一些可以模拟的其他功能封装此基本调用:

class ComplexObject : public SimpleObject
{
public:
    virtual void moveX(int dX)
    {
        // Call base function
        baseMoveX(dx);
        // Do something specific
    }
protected: 
    virtual void baseMoveX(int dX)
    {
        SimpleObject::moveX(dx);
    }
};

然后在您的Testable类中,只需模拟此基本功能:

class TestableComplexObject : public ComplexObject
{
public:
    MOCK_METHOD1(baseMoveX, void(int dX));
};

您不能仅模拟moveX,因为 - 无法区分基础和在这种情况下派生。

所以 - 您的测试看起来像这样:

TEST_F(ATestableComplexObject, CallsBaseClassMoveXWhenMoveXIsCalled)
{
    int dX(8);
    TestableComplexObject obj;
    EXPECT_CALL(obj, baseMoveX(dX))
                .WillOnce(testing::Invoke([&obj] (auto dx) {obj.SimpleObject::moveX(dx); }));
    obj.moveX();
}

[update]

在注释中发现 - 仍然存在一个问题,如何确保ConspectObject :: BaseMovex()调用SimpleObject :: Movex。

可能的解决方案是在复杂对象和SimpleObject之间再将一个类放置。

template <typename BaseObject>
class IntermediateObject : public BaseObject
{
public:
    virtual void baseMoveX(int dX)
    {
        BaseObject::moveX(dx);
    }
};

通过测试确保这确实发生了:

class TestableBaseMock
{
public:
    MOCK_METHOD1(moveX, void(int dX));
};
TEST(IntermediateObject Test, shallCallBaseMoveX)
{
    const int dX = 8;
    IntermediateObject<TestableBaseMock> objectUnderTest;
    TestableBaseMock& baseMock = objectUnderTest;
    EXPECT_CALL(baseMock, moveX(dX));
    objectUnderTest.baseMoveX(dx);
}

然后 - 将其放在简单和复杂的类之间:

class ComplexObject : public IntermediateObject<SimpleObject>
{
public:
    virtual void moveX(int dX)
    {
        // Call base function
        baseMoveX(dx);
        // Do something specific
    }
};

最后 - 我只想强调改变您的原始设计 - 使用汇总而不是继承(又称装饰器设计模式)是最好的方法。首先 - 正如您在我的回答中看到的 - 尝试使用继承使设计更糟,如果我们想测试它。第二 - 装饰器设计的测试要简单得多,就像答案之一中所示...

这里的主要问题是,在方法TestableComplexObject::doMoveX中,您正在调用方法SimpleObject::moveX。这就是为什么一切都过去了。您应该调用属于ComplexObject类的方法moveX。因此,将方法doMoveX更改为:

void doMoveX(int dX)
{
    ComplexObject::moveX(dX);
}

将解决您的问题。

您发布的代码还有一个问题。测试体中的最后一个说法应为:

obj.moveX(dX);

,但我想这只是编写问题时犯的错误?

希望这会有所帮助!

最新更新