【问题标题】:C++, Googlemock - testing local objectC++,Googlemock - 测试本地对象
【发布时间】:2016-08-22 03:20:12
【问题描述】:

我开始使用 googletest 和 googlemock 库,但遇到了一个无法解决的问题。我有一个类似这样的代码:

class Painter
{
  public:
  void DrawSomething();
};

void Painter::DrawSomething()
{
  Turtle turtle;
 turtle.doSomething();
}

main()
{
  Painter p;
  p.DrawSomething();
}

我已经模拟了 Turtle 类,但是当本地创建 turtle 的对象时,如何测试 doSomething() 方法(例如使用 EXPECT_CALL)?是否可以不修改 Painter 类?

感谢您的回答。

【问题讨论】:

  • 为什么不明确地测试 doSomething 和 Turtle 类,而不是通过对 Painter 的测试?
  • DrawSomething 方法有更多说明,我想使用一些海龟方法绘制一个矩形,例如 move()、turn()、penDown() 等,我想测试它们是否在DrawSomething 被调用。
  • 嗯,模拟类需要通过接口进行通信。您应该引入一个ITurtle 接口(抽象类),它被传递给void Painter::DrawSomething() 或由Painter 作为引用成员变量持有。

标签: c++ unit-testing googletest googlemock gmock


【解决方案1】:

我嘲笑过 Turtle 类...

你究竟是如何模拟它的?

...但是当本地创建turtle对象时,如何测试doSomething()方法(例如EXPECT_CALL)? 是否可以不修改 Painter 类?
(强调我的)

直接的答案是:

如果不通过接口解耦,你不能神奇地注入一个模拟来代替另一个类中使用的真实实例


你应该有类似下面的代码:

struct ITurtle {
    virtual void PenUp() = 0;
    virtual void PenDown() = 0;
    virtual void TurnLeft(double degrees) = 0;
    virtual void Move(double distance) = 0;
    // ...
    virtual ~ITurtle() {}
};

struct TurtleMock : ITurtle {
    // Mock method declarations
    MOCK_METHOD0(PenUp, void ());
    MOCK_METHOD0(PenDown, void ());
    MOCK_METHOD1(TurnLeft, void (double));
    MOCK_METHOD1(Move, void (double));
};

class Turtle : public ITurtle {
public:
    void PenUp();
    void PenDown();
    void TurnLeft(double degrees);
    void Move(double distance);
};

在单独的翻译单元中为上述声明提供 real 实现。


class Painter {
public:
    Painter(ITurtle& turtle) : turtle_(turtle) {}   
    void DrawSomething();
private:
    ITurtle& turtle_;
};

void Painter::DrawSomething() {
    turtle_.PenDown();
    turtle_.TurnLeft(30.0);
    turtle_.Move(10.0);
    turtle_.TurnLeft(30.0);
    turtle_.Move(10.0);
    // ...
}

您也可以将ITurtle 接口传递给DrawSomething() 函数:

class Painter {
public:
    void DrawSomething(ITurtle& turtle);
};

void Painter::DrawSomething(ITurtle& turtle) {
    turtle.PenDown();
    turtle.TurnLeft(30.0);
    turtle.Move(10.0);
    turtle.TurnLeft(30.0);
    turtle.Move(10.0);
    // ...
}

int main() {
     NiceMock<TurtleMock> turtle;
     Painter p(turtle);
     // Painter p; <<< for the alternative solution

     EXPECT_CALL(turtle,PenDown())
         .Times(1);
     EXPECT_CALL(turtle,TurnLeft(_))
         .Times(2);
     EXPECT_CALL(turtle,Move(_))
         .Times(2);

     p.DrawSomething();
     //  p.DrawSomething(turtle); <<< for the alternative solution

}

【讨论】:

  • 非常感谢您的详细解释,我完全按照您在 TurtleMock 课程中所做的那样模拟了乌龟。只有一个问题:假设我在 Painter 类中没有 ITurtle 成员,并且 Turtle 对象仅在 DrawSomething 函数中创建。如果我想将模拟对象传递给这个方法,唯一的方法是让 DrawSomething(ITurtle& turtle)?
  • @mcjay “唯一的办法是让DrawSomething(ITurtle&amp; turtle)?” 是的,这是另一种选择。
【解决方案2】:

我编写了一个包装类来模拟生产代码而不更改它。请让我知道这是否有任何缺陷。

#include "gtest/gtest.h"
#include "src/gtest-all.cc"
#include "src/gmock-all.cc"
#include "src/gmock_main.cc"
#include <iostream>
#include <string>
#include <vector>

using ::testing::An;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SetArgReferee;

using namespace std;

class Student
{
    int iAge;

    public:
    Student(int _iAge) : iAge(_iAge)
    {
    }

    virtual void PrintDetails()
    {
        cout<<"Age:"<<iAge<<endl;
    }
    virtual bool CheckGrade(int iGrade)
    {
        return (iGrade - 5) == iAge;
    }
};

class StudentFaker
{
    static Student* internalObject;

    public:

    static void FakerSetObject(Student* object) {
        internalObject = object;
    }

    StudentFaker(int _iAge){
    }

    void PrintDetails()  {
        internalObject->PrintDetails();
    }

    bool CheckGrade(int iGrade)  {
        return internalObject->CheckGrade(iGrade);
    }
};

Student* StudentFaker::internalObject = NULL;

class StudentMock : public Student
{
    public:
    StudentMock(int _iAge) : Student(_iAge) { }

    MOCK_METHOD0(PrintDetails,void());
    MOCK_METHOD1(CheckGrade,bool(int));
};

#define UNITTEST

bool ProductionCode();

TEST(STUDENT,TEST)
{
    StudentMock stMock(8);

    EXPECT_CALL(stMock, PrintDetails())
        .Times(AtLeast(1))
        .WillOnce(Return());

    EXPECT_CALL(stMock, CheckGrade(5))
        .Times(AtLeast(1))
        .WillOnce(Return(true));

    StudentFaker::FakerSetObject(&stMock);

    EXPECT_TRUE(ProductionCode());
}

//Production code
#ifdef UNITTEST
#define Student StudentFaker
#endif

bool ProductionCode()
{
    Student st(8);
    st.PrintDetails();
    if(st.CheckGrade(5))
        return true;
    else
        return false;
}

//Production code

【讨论】:

    猜你喜欢
    • 2022-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-01
    相关资源
    最近更新 更多